# -*- coding: utf-8 -*-

import collections

import torch
import torch.nn as nn

from models.base import HeadBase
from layers.core import Flatten
from utils.initialization import initialize_weights


class LinearHead(HeadBase):
    def __init__(self, in_channels: int, num_features: int, dropout: float = 0.0):
        """
        Arguments:
            in_channels: int, number of input feature maps.
            num_features: int, number of output features.
        """
        super(LinearHead, self).__init__(num_features)

        self.in_channels = in_channels
        self.num_features = num_features
        self.dropout = dropout
        self.layers = self.make_layers(
            in_channels=self.in_channels,
            num_features=self.num_features,
            dropout=self.dropout,
        )
        initialize_weights(self.layers)

    def forward(self, x: torch.FloatTensor):
        return self.layers(x)

    @staticmethod
    def make_layers(in_channels: int, num_features: int, dropout: float = 0.0):
        layers = nn.Sequential(
            collections.OrderedDict(
                [
                    ('gap', nn.AdaptiveAvgPool2d(1)),
                    ('flatten', Flatten()),
                    ('dropout', nn.Dropout(p=dropout)),
                    ('linear', nn.Linear(in_channels, num_features))
                ]
            )
        )

        return layers

    @property
    def num_parameters(self):
        return sum(p.numel() for p in self.parameters() if p.requires_grad)


class LinearClassifier(LinearHead):
    def __init__(self, in_channels: int, num_classes: int, dropout: float = 0.):
        """
        Arguments:
            in_channels: int, number of input feature maps.
            num_classes: int, number of classes.
        """
        super(LinearClassifier, self).__init__(in_channels, num_classes, dropout)

    @property
    def num_classes(self):
        return self.num_features


class MLPHead(HeadBase):
    def __init__(self, in_channels: int, num_features: int):
        """
        Arguments:
            in_channels: int, number of input feature maps.
            num_features: int, number of output units.
        """
        super(MLPHead, self).__init__(num_features)

        self.in_channels = in_channels
        self.num_features = num_features
        self.layers = self.make_layers(
            in_channels=self.in_channels,
            num_features=self.num_features,
        )

    def forward(self, x: torch.FloatTensor):
        return self.layers(x)

    @staticmethod
    def make_layers(in_channels: int, num_features: int):
        layers = nn.Sequential(
            collections.OrderedDict(
                [
                    ('gap', nn.AdaptiveAvgPool2d(1)),
                    ('flatten', Flatten()),
                    ('linear1', nn.Linear(in_channels, in_channels, bias=False)),
                    ('bnorm1', nn.BatchNorm1d(in_channels)),
                    ('relu1', nn.ReLU(inplace=True)),
                    ('linear2', nn.Linear(in_channels, num_features, bias=True))
                ]
            )
        )

        return layers

    @property
    def num_parameters(self):
        return sum(p.numel() for p in self.layers.parameters() if p.requires_grad)

0de70d9dcb7b4211d31d2873a8dfd90418c3af58
<mask token>


class LinearClassifier(LinearHead):

    def __init__(self, in_channels: int, num_classes: int, dropout: float=0.0):
        """
        Arguments:
            in_channels: int, number of input feature maps.
            num_classes: int, number of classes.
        """
        super(LinearClassifier, self).__init__(in_channels, num_classes,
            dropout)

    @property
    def num_classes(self):
        return self.num_features


class MLPHead(HeadBase):

    def __init__(self, in_channels: int, num_features: int):
        """
        Arguments:
            in_channels: int, number of input feature maps.
            num_features: int, number of output units.
        """
        super(MLPHead, self).__init__(num_features)
        self.in_channels = in_channels
        self.num_features = num_features
        self.layers = self.make_layers(in_channels=self.in_channels,
            num_features=self.num_features)

    def forward(self, x: torch.FloatTensor):
        return self.layers(x)

    @staticmethod
    def make_layers(in_channels: int, num_features: int):
        layers = nn.Sequential(collections.OrderedDict([('gap', nn.
            AdaptiveAvgPool2d(1)), ('flatten', Flatten()), ('linear1', nn.
            Linear(in_channels, in_channels, bias=False)), ('bnorm1', nn.
            BatchNorm1d(in_channels)), ('relu1', nn.ReLU(inplace=True)), (
            'linear2', nn.Linear(in_channels, num_features, bias=True))]))
        return layers

    @property
    def num_parameters(self):
        return sum(p.numel() for p in self.layers.parameters() if p.
            requires_grad)

<mask token>


class LinearHead(HeadBase):

    def __init__(self, in_channels: int, num_features: int, dropout: float=0.0
        ):
        """
        Arguments:
            in_channels: int, number of input feature maps.
            num_features: int, number of output features.
        """
        super(LinearHead, self).__init__(num_features)
        self.in_channels = in_channels
        self.num_features = num_features
        self.dropout = dropout
        self.layers = self.make_layers(in_channels=self.in_channels,
            num_features=self.num_features, dropout=self.dropout)
        initialize_weights(self.layers)
    <mask token>
    <mask token>
    <mask token>


class LinearClassifier(LinearHead):

    def __init__(self, in_channels: int, num_classes: int, dropout: float=0.0):
        """
        Arguments:
            in_channels: int, number of input feature maps.
            num_classes: int, number of classes.
        """
        super(LinearClassifier, self).__init__(in_channels, num_classes,
            dropout)

    @property
    def num_classes(self):
        return self.num_features


class MLPHead(HeadBase):

    def __init__(self, in_channels: int, num_features: int):
        """
        Arguments:
            in_channels: int, number of input feature maps.
            num_features: int, number of output units.
        """
        super(MLPHead, self).__init__(num_features)
        self.in_channels = in_channels
        self.num_features = num_features
        self.layers = self.make_layers(in_channels=self.in_channels,
            num_features=self.num_features)

    def forward(self, x: torch.FloatTensor):
        return self.layers(x)

    @staticmethod
    def make_layers(in_channels: int, num_features: int):
        layers = nn.Sequential(collections.OrderedDict([('gap', nn.
            AdaptiveAvgPool2d(1)), ('flatten', Flatten()), ('linear1', nn.
            Linear(in_channels, in_channels, bias=False)), ('bnorm1', nn.
            BatchNorm1d(in_channels)), ('relu1', nn.ReLU(inplace=True)), (
            'linear2', nn.Linear(in_channels, num_features, bias=True))]))
        return layers

    @property
    def num_parameters(self):
        return sum(p.numel() for p in self.layers.parameters() if p.
            requires_grad)

<mask token>


class LinearHead(HeadBase):

    def __init__(self, in_channels: int, num_features: int, dropout: float=0.0
        ):
        """
        Arguments:
            in_channels: int, number of input feature maps.
            num_features: int, number of output features.
        """
        super(LinearHead, self).__init__(num_features)
        self.in_channels = in_channels
        self.num_features = num_features
        self.dropout = dropout
        self.layers = self.make_layers(in_channels=self.in_channels,
            num_features=self.num_features, dropout=self.dropout)
        initialize_weights(self.layers)

    def forward(self, x: torch.FloatTensor):
        return self.layers(x)

    @staticmethod
    def make_layers(in_channels: int, num_features: int, dropout: float=0.0):
        layers = nn.Sequential(collections.OrderedDict([('gap', nn.
            AdaptiveAvgPool2d(1)), ('flatten', Flatten()), ('dropout', nn.
            Dropout(p=dropout)), ('linear', nn.Linear(in_channels,
            num_features))]))
        return layers
    <mask token>


class LinearClassifier(LinearHead):

    def __init__(self, in_channels: int, num_classes: int, dropout: float=0.0):
        """
        Arguments:
            in_channels: int, number of input feature maps.
            num_classes: int, number of classes.
        """
        super(LinearClassifier, self).__init__(in_channels, num_classes,
            dropout)

    @property
    def num_classes(self):
        return self.num_features


class MLPHead(HeadBase):

    def __init__(self, in_channels: int, num_features: int):
        """
        Arguments:
            in_channels: int, number of input feature maps.
            num_features: int, number of output units.
        """
        super(MLPHead, self).__init__(num_features)
        self.in_channels = in_channels
        self.num_features = num_features
        self.layers = self.make_layers(in_channels=self.in_channels,
            num_features=self.num_features)

    def forward(self, x: torch.FloatTensor):
        return self.layers(x)

    @staticmethod
    def make_layers(in_channels: int, num_features: int):
        layers = nn.Sequential(collections.OrderedDict([('gap', nn.
            AdaptiveAvgPool2d(1)), ('flatten', Flatten()), ('linear1', nn.
            Linear(in_channels, in_channels, bias=False)), ('bnorm1', nn.
            BatchNorm1d(in_channels)), ('relu1', nn.ReLU(inplace=True)), (
            'linear2', nn.Linear(in_channels, num_features, bias=True))]))
        return layers

    @property
    def num_parameters(self):
        return sum(p.numel() for p in self.layers.parameters() if p.
            requires_grad)

<mask token>


class LinearHead(HeadBase):

    def __init__(self, in_channels: int, num_features: int, dropout: float=0.0
        ):
        """
        Arguments:
            in_channels: int, number of input feature maps.
            num_features: int, number of output features.
        """
        super(LinearHead, self).__init__(num_features)
        self.in_channels = in_channels
        self.num_features = num_features
        self.dropout = dropout
        self.layers = self.make_layers(in_channels=self.in_channels,
            num_features=self.num_features, dropout=self.dropout)
        initialize_weights(self.layers)

    def forward(self, x: torch.FloatTensor):
        return self.layers(x)

    @staticmethod
    def make_layers(in_channels: int, num_features: int, dropout: float=0.0):
        layers = nn.Sequential(collections.OrderedDict([('gap', nn.
            AdaptiveAvgPool2d(1)), ('flatten', Flatten()), ('dropout', nn.
            Dropout(p=dropout)), ('linear', nn.Linear(in_channels,
            num_features))]))
        return layers

    @property
    def num_parameters(self):
        return sum(p.numel() for p in self.parameters() if p.requires_grad)


class LinearClassifier(LinearHead):

    def __init__(self, in_channels: int, num_classes: int, dropout: float=0.0):
        """
        Arguments:
            in_channels: int, number of input feature maps.
            num_classes: int, number of classes.
        """
        super(LinearClassifier, self).__init__(in_channels, num_classes,
            dropout)

    @property
    def num_classes(self):
        return self.num_features


class MLPHead(HeadBase):

    def __init__(self, in_channels: int, num_features: int):
        """
        Arguments:
            in_channels: int, number of input feature maps.
            num_features: int, number of output units.
        """
        super(MLPHead, self).__init__(num_features)
        self.in_channels = in_channels
        self.num_features = num_features
        self.layers = self.make_layers(in_channels=self.in_channels,
            num_features=self.num_features)

    def forward(self, x: torch.FloatTensor):
        return self.layers(x)

    @staticmethod
    def make_layers(in_channels: int, num_features: int):
        layers = nn.Sequential(collections.OrderedDict([('gap', nn.
            AdaptiveAvgPool2d(1)), ('flatten', Flatten()), ('linear1', nn.
            Linear(in_channels, in_channels, bias=False)), ('bnorm1', nn.
            BatchNorm1d(in_channels)), ('relu1', nn.ReLU(inplace=True)), (
            'linear2', nn.Linear(in_channels, num_features, bias=True))]))
        return layers

    @property
    def num_parameters(self):
        return sum(p.numel() for p in self.layers.parameters() if p.
            requires_grad)

# -*- coding: utf-8 -*-

import collections

import torch
import torch.nn as nn

from models.base import HeadBase
from layers.core import Flatten
from utils.initialization import initialize_weights


class LinearHead(HeadBase):
    def __init__(self, in_channels: int, num_features: int, dropout: float = 0.0):
        """
        Arguments:
            in_channels: int, number of input feature maps.
            num_features: int, number of output features.
        """
        super(LinearHead, self).__init__(num_features)

        self.in_channels = in_channels
        self.num_features = num_features
        self.dropout = dropout
        self.layers = self.make_layers(
            in_channels=self.in_channels,
            num_features=self.num_features,
            dropout=self.dropout,
        )
        initialize_weights(self.layers)

    def forward(self, x: torch.FloatTensor):
        return self.layers(x)

    @staticmethod
    def make_layers(in_channels: int, num_features: int, dropout: float = 0.0):
        layers = nn.Sequential(
            collections.OrderedDict(
                [
                    ('gap', nn.AdaptiveAvgPool2d(1)),
                    ('flatten', Flatten()),
                    ('dropout', nn.Dropout(p=dropout)),
                    ('linear', nn.Linear(in_channels, num_features))
                ]
            )
        )

        return layers

    @property
    def num_parameters(self):
        return sum(p.numel() for p in self.parameters() if p.requires_grad)


class LinearClassifier(LinearHead):
    def __init__(self, in_channels: int, num_classes: int, dropout: float = 0.):
        """
        Arguments:
            in_channels: int, number of input feature maps.
            num_classes: int, number of classes.
        """
        super(LinearClassifier, self).__init__(in_channels, num_classes, dropout)

    @property
    def num_classes(self):
        return self.num_features


class MLPHead(HeadBase):
    def __init__(self, in_channels: int, num_features: int):
        """
        Arguments:
            in_channels: int, number of input feature maps.
            num_features: int, number of output units.
        """
        super(MLPHead, self).__init__(num_features)

        self.in_channels = in_channels
        self.num_features = num_features
        self.layers = self.make_layers(
            in_channels=self.in_channels,
            num_features=self.num_features,
        )

    def forward(self, x: torch.FloatTensor):
        return self.layers(x)

    @staticmethod
    def make_layers(in_channels: int, num_features: int):
        layers = nn.Sequential(
            collections.OrderedDict(
                [
                    ('gap', nn.AdaptiveAvgPool2d(1)),
                    ('flatten', Flatten()),
                    ('linear1', nn.Linear(in_channels, in_channels, bias=False)),
                    ('bnorm1', nn.BatchNorm1d(in_channels)),
                    ('relu1', nn.ReLU(inplace=True)),
                    ('linear2', nn.Linear(in_channels, num_features, bias=True))
                ]
            )
        )

        return layers

    @property
    def num_parameters(self):
        return sum(p.numel() for p in self.layers.parameters() if p.requires_grad)
