File size: 4,884 Bytes
dcfb67c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import torch
import torch.nn as nn
import torchvision.models as models
from modelscope.msdatasets import MsDataset
from utils import MODEL_DIR


class EvalNet:
    model: nn.Module = None
    m_type = "squeezenet"
    input_size = 224
    output_size = 512

    def __init__(self, log_name: str, cls_num: int):
        saved_model_path = f"{MODEL_DIR}/{log_name}/save.pt"
        m_ver = "_".join(log_name.split("_")[:-3])
        self.m_type, self.input_size = self._model_info(m_ver)

        if not hasattr(models, m_ver):
            raise Exception("Unsupported model.")

        self.model = eval("models.%s()" % m_ver)
        linear_output = self._set_outsize()
        self._set_classifier(cls_num, linear_output)
        checkpoint = torch.load(saved_model_path, map_location="cpu")
        if torch.cuda.is_available():
            checkpoint = torch.load(saved_model_path)

        self.model.load_state_dict(checkpoint, False)
        self.model.eval()

    def _get_backbone(self, ver: str, backbone_list: list):
        for bb in backbone_list:
            if ver == bb["ver"]:
                return bb

        print("Backbone name not found, using default option - alexnet.")
        return backbone_list[0]

    def _model_info(self, m_ver: str):
        backbone_list = MsDataset.load(
            "monetjoe/cv_backbones",
            split="v1",
        )
        backbone = self._get_backbone(m_ver, backbone_list)
        m_type = str(backbone["type"])
        input_size = int(backbone["input_size"])
        return m_type, input_size

    def _classifier(self, cls_num: int, output_size: int, linear_output: bool):
        q = (1.0 * output_size / cls_num) ** 0.25
        l1 = int(q * cls_num)
        l2 = int(q * l1)
        l3 = int(q * l2)
        if linear_output:
            return torch.nn.Sequential(
                nn.Dropout(),
                nn.Linear(output_size, l3),
                nn.ReLU(inplace=True),
                nn.Dropout(),
                nn.Linear(l3, l2),
                nn.ReLU(inplace=True),
                nn.Dropout(),
                nn.Linear(l2, l1),
                nn.ReLU(inplace=True),
                nn.Linear(l1, cls_num),
            )

        else:
            return torch.nn.Sequential(
                nn.Dropout(),
                nn.Conv2d(output_size, l3, kernel_size=(1, 1), stride=(1, 1)),
                nn.ReLU(inplace=True),
                nn.AdaptiveAvgPool2d(output_size=(1, 1)),
                nn.Flatten(),
                nn.Linear(l3, l2),
                nn.ReLU(inplace=True),
                nn.Dropout(),
                nn.Linear(l2, l1),
                nn.ReLU(inplace=True),
                nn.Linear(l1, cls_num),
            )

    def _set_outsize(self):
        for name, module in self.model.named_modules():
            if (
                str(name).__contains__("classifier")
                or str(name).__eq__("fc")
                or str(name).__contains__("head")
                or hasattr(module, "classifier")
            ):
                if isinstance(module, torch.nn.Linear):
                    self.output_size = module.in_features
                    return True

                if isinstance(module, torch.nn.Conv2d):
                    self.output_size = module.in_channels
                    return False

        return False

    def _set_classifier(self, cls_num: int, linear_output: bool):
        if self.m_type == "convnext":
            del self.model.classifier[2]
            self.model.classifier = nn.Sequential(
                *list(self.model.classifier)
                + list(self._classifier(cls_num, self.output_size, linear_output))
            )
            return

        elif self.m_type == "maxvit":
            del self.model.classifier[5]
            self.model.classifier = nn.Sequential(
                *list(self.model.classifier)
                + list(self._classifier(cls_num, self.output_size, linear_output))
            )
            return

        if hasattr(self.model, "classifier"):
            self.model.classifier = self._classifier(
                cls_num, self.output_size, linear_output
            )
            return

        elif hasattr(self.model, "fc"):
            self.model.fc = self._classifier(cls_num, self.output_size, linear_output)
            return

        elif hasattr(self.model, "head"):
            self.model.head = self._classifier(cls_num, self.output_size, linear_output)
            return

        self.model.heads.head = self._classifier(
            cls_num, self.output_size, linear_output
        )

    def forward(self, x: torch.Tensor):
        if torch.cuda.is_available():
            x = x.cuda()
            self.model = self.model.cuda()

        if self.m_type == "googlenet":
            return self.model(x)[0]
        else:
            return self.model(x)