Why no nn.BufferList like function for registered buffer tensor?

dragen: <p>For running mean/variance like tensor, we can use <code>self.register_buffer(name, tensor)</code> to manger it.<br>
However, <code>self.register_buffer</code> must use name to refer to the tensor. In some case, I have a list of buffer tensors and I dnt want to use name to manager these buffers, for instance:</p>
<pre><code class="lang-python">class MyModule(nn.Module):
  def __init__(n):
         self.params = [tensor(3, 3) for i  in range(n)]
         for i,p in enumerate(self.params):
             self.register_buffer('tensor'+str(i), p)

</code></pre>
<p>and then I want to visit <code>self.params</code>. However, once we call <code>MyModule().to('cuda')</code>, the tensor in <code>self.params</code> still point to initial cpu tensor, and we need to use <code>self.tensor0/1/...</code> to refer to cuda tensor. That’s not I wanted.<br>
I wish to refer to these tensor by <code>self.params</code> still.<br>
Could anyone help me.</p>

justusschock: <pre><code class="lang-auto">class MyModule(nn.Module):
  def __init__(n):
         self.params = [] 
         _params = [tensor(3, 3) for i  in range(n) ]
         for i,p in enumerate(_params):
             self.register_buffer('tensor'+str(i), p)
             self.params.append(getattr(self, 'tensor' + str(I))) 
         
</code></pre>
<p>Should work. I haven’t tested it with modifying the params but since lists usually only store references it should work this way</p>

alexis-jacq: <p>This would not work, you will obtain something like ‘Overflow when unpacking long’ (because <code>params</code> is pointing to un-initialized values that are too long to be translated into int for the print).</p>
<p>I would suggere something like this for a simple access from index:</p>
<pre><code class="lang-python">class MyModule(nn.Module):
    def __init__(self,n):
        super(MyModule, self).__init__()
        _params = [torch.Tensor(3, 3) for i  in range(n) ]
        for i,p in enumerate(_params):
            self.register_buffer('tensor'+str(i), p)
 
    def params(self, i):
        return self.__getattr__('tensor'+str(i))
</code></pre>
<p>And if you want the table structure in order to loop, then you can return an iterator with <code>yield</code>:</p>
<pre><code class="lang-python">class MyModule(nn.Module):
    def __init__(self,n):
        super(MyModule, self).__init__()
        self.n = n
        _params = [torch.Tensor(3, 3) for i  in range(n) ]
        for i,p in enumerate(_params):
            self.register_buffer('tensor'+str(i), p)
 
    def params(self):
        for i in range(self.n):
            yield self.__getattr__('tensor'+str(i))
</code></pre>

SimonW: <p>The code <a class="mention" href="/u/justusschock">@justusschock</a> posted works for the purpose of registering buffers. The printing error you saw is already fixed.</p>

alexis-jacq: <p>Ah cool ! I should update then</p>

Xonobo_Xonobo: <p>When I move the module to cuda the registered tensors becomes cuda tensors but the tensors in the list of self.params stays in cpu. This is not expected, I guess.</p>

liangbright: <p>It does not work in Pytorch 1.6</p>

andre-vauvelle: <p>Your second implementation with the generator worked for me when creating “list buffer tensors” that work with pytorch lightning trainers. <img src="https://discuss.pytorch.org/images/emoji/apple/raised_hands.png?v=12" title=":raised_hands:" class="emoji" alt=":raised_hands:" loading="lazy" width="20" height="20"></p>
<p>I required a while loop to fully replicate the functionality I needed from a list, otherwise, StopIteration errors occur on repeated access.</p>
<pre><code class="lang-auto">class MyModule(nn.Module):
    def __init__(self,n):
        super(MyModule, self).__init__()
        self.n = n
        _params = [torch.Tensor(3, 3) for i  in range(n) ]
        for i,p in enumerate(_params):
            self.register_buffer('tensor'+str(i), p)
 
    def params(self):
        while True:
            for i in range(self.n):
                yield self.__getattr__('tensor'+str(i))
</code></pre>

offchan: <p>For anyone reading this in the future, just use this code (because I’ve tested it) and understand that one of the benefits of using buffer is to automatically update dtype and device of your float attributes when <code>.to()</code> is called on the module.<br>
Copy it and try it in your notebook.</p>
<pre><code class="lang-py">import torch
from torch import nn

class MyModule(nn.Module):
    def __init__(self, n):
        super().__init__()
        self.n = n
        params = [torch.randn(3, 3) for i  in range(n) ]
        for i,p in enumerate(params):
            self.register_buffer(f"param_{i}", p)

    @property
    def params(self):
        return [getattr(self, f"param_{i}") for i in range(self.n)]


m = MyModule(3)
print(m.params[0].dtype, m.params[0].device)  # torch.float32 cpu
m.to("cuda")
print(m.params[0].dtype, m.params[0].device)  # torch.float32 cuda:0
m.to(torch.float64)
print(m.params[0].dtype, m.params[0].device)  # torch.float64 cuda:0
</code></pre>

hellolzc: <p>I found that there is already an API named <code>nn.ParameterList</code>. Could the developers of pytorch add the API <code>nn.BufferList</code>? Everyone who comes to this webpage needs it.</p>

rusty_penguin: <p>If your list of tensors isn’t entirely arbitrary you can always use nested tensors instead.</p>
<pre><code class="lang-auto">import torch
from torch import nn, Tensor

class Network(nn.Module):
    def __init__(self, data: list[Tensor]):
        super().__init__()

        self.register_buffer(
            "data", torch.nested.as_nested_tensor(data)
        )

</code></pre>
<p>Naturally the standard warnings about using prototype / unstable features apply.</p>