File size: 5,008 Bytes
353de0b
 
 
a0eed22
35eaf01
a0eed22
353de0b
 
 
 
 
 
 
 
 
 
 
 
 
 
a0eed22
353de0b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a0eed22
353de0b
 
 
a0eed22
 
 
 
 
353de0b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
147
148
import torch
import torch.nn as nn
import torchvision.models as models
from modelscope.msdatasets import MsDataset
from datasets import load_dataset
from utils import MODEL_DIR, EN_US


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 ValueError("不支持的模型")

        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("未找到骨干网络名称,使用默认选项 - alexnet")
        return backbone_list[0]

    def _model_info(self, m_ver: str):
        backbone_list = (
            load_dataset("monetjoe/cv_backbones", split="train")
            if EN_US
            else 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)