Home > Enterprise >  How self.anchors is not self. declared in the __init__ but it's used in the method?
How self.anchors is not self. declared in the __init__ but it's used in the method?

Time:08-25

class Detect(nn.Module):
stride = None  # strides computed during build
onnx_dynamic = False  # ONNX export parameter
export = False  # export mode

def __init__(self, nc=80, anchors=(), ch=(), inplace=True):  # detection layer
    super().__init__()
    self.nc = nc  # number of classes
    self.no = nc   5  # number of outputs per anchor
    self.nl = len(anchors)  # number of detection layers
    self.na = len(anchors[0]) // 2  # number of anchors
    self.grid = [torch.zeros(1)] * self.nl  # init grid
    self.anchor_grid = [torch.zeros(1)] * self.nl  # init anchor grid
    self.register_buffer('anchors', torch.tensor(anchors).float().view(self.nl, -1, 2))  # shape(nl,na,2)
    self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch)  # output conv
    self.inplace = inplace  # use inplace ops (e.g. slice assignment)

def _make_grid(self, nx=20, ny=20, i=0):
    d = self.anchors[i].device
    t = self.anchors[i].dtype

    shape = 1, self.na, ny, nx, 2  # grid shape
    y, x = torch.arange(ny, device=d, dtype=t), torch.arange(nx, device=d, dtype=t)
    if check_version(torch.__version__, '1.10.0'):  # torch>=1.10.0 meshgrid workaround for torch>=0.7 compatibility
        yv, xv = torch.meshgrid(y, x, indexing='ij')
    else:
        yv, xv = torch.meshgrid(y, x)
    grid = torch.stack((xv, yv), 2).expand(shape) - 0.5  # add grid offset, i.e. y = 2.0 * x - 0.5
    anchor_grid = (self.anchors[i] * self.stride[i]).view((1, self.na, 1, 1, 2)).expand(shape)
    return grid, anchor_grid

In the init there's no self.anchors = anchors but in the _make_grid() method they are using self.anchors. How is it possible? PS I had to drop the forward method otherwise stackoverflow claimed there was too much code

CodePudding user response:

Notice this call in __init__:

self.register_buffer('anchors', torch.tensor(anchors).float().view(self.nl, -1, 2))  # shape(nl,na,2)

Let's see what the PyTorch docs have to say about nn.Module.register_buffer:

register_buffer(name, tensor, persistent=True)
Adds a buffer to the module.

This is typically used to register a buffer that should not to be considered a model parameter. For example, BatchNorm’s running_mean is not a parameter, but is part of the module’s state. Buffers, by default, are persistent and will be saved alongside parameters. This behavior can be changed by setting persistent to False. The only difference between a persistent buffer and a non-persistent buffer is that the latter will not be a part of this module’s state_dict.

Buffers can be accessed as attributes using given names.

Parameters

  • name (string) – name of the buffer. The buffer can be accessed from this module using the given name

[...]

So it appears the base class you're inheriting from exposes any registered buffer as an attribute, hence the availability of self.anchors at runtime

  • Related