File size: 7,302 Bytes
9e14838
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
import gc
import math

# Simplified config for inference
config_text = """

DATA:

  NUM_FRAMES: 8

  SAMPLING_RATE: 8

  TEST_CROP_SIZE: 256

  INPUT_CHANNEL_NUM: [3]

RESNET:

  ZERO_INIT_FINAL_BN: True

  WIDTH_PER_GROUP: 64

  NUM_GROUPS: 1

  DEPTH: 50

  TRANS_FUNC: bottleneck_transform

  STRIDE_1X1: False

  NUM_BLOCK_TEMP_KERNEL: [[3], [4], [6], [3]]

NONLOCAL:

  LOCATION: [[[]], [[]], [[]], [[]]]

  GROUP: [[1], [1], [1], [1]]

  INSTANTIATION: softmax

BN:

  USE_PRECISE_STATS: True

  NUM_BATCHES_PRECISE: 200

MODEL:

  NUM_CLASSES: 1

  ARCH: i3d

  MODEL_NAME: ResNet

  DROPOUT_RATE: 0.1

  HEAD_ACT: sigmoid

TEST:

  ENABLE: True

  DATASET: kinetics

  BATCH_SIZE: 64

DATA_LOADER:

  NUM_WORKERS: 8

  PIN_MEMORY: True

NUM_GPUS: 8

NUM_SHARDS: 1

RNG_SEED: 0

OUTPUT_DIR: .

"""

# from .f3net import FAD_Head
from slowfast.models.video_model_builder import ResNet as ResNetOri
from slowfast.config.defaults import get_cfg
import torch
from torch import nn
from config_ftcn import config as my_cfg
from inspect import signature
# Removed TimeTransformer import - not used directly in this file
# Removed random import - not used in inference

my_cfg.init_with_yaml()
my_cfg.update_with_yaml("ftcn_tt.yaml")
my_cfg.freeze()



class CenterPatchPool(nn.Module):
    """Simplified patch pooling for inference - always use center patch"""
    def __init__(self):
        super().__init__()

    def forward(self, x):
        # batch,channel,16,7x7
        b, c, t, h, w = x.shape
        x = x.reshape(b, c, t, h * w)
        idx = h * w // 2  # Always use center patch for inference
        x = x[..., idx]
        return x


def valid_idx(idx, h):
    i = idx // h
    j = idx % h
    if j == 0 or i == h - 1 or j == h - 1:
        return False
    else:
        return True


class CenterAvgPool(nn.Module):
    """Simplified average pooling for inference - use all valid patches"""
    def __init__(self):
        super().__init__()

    def forward(self, x):
        # batch,channel,16,7x7
        b, c, t, h, w = x.shape
        x = x.reshape(b, c, t, h * w)
        candidates = list(range(h * w))
        candidates = [idx for idx in candidates if valid_idx(idx, h)]
        x = x[..., candidates].mean(-1)
        return x
    
        

# Removed duplicate TransformerHead class - using the one from temporal_transformer.py 


parameters = [parameter for parameter in signature(nn.Conv3d).parameters]

spatial_count = my_cfg.model.inco.spatial_count
keep_stride_count = my_cfg.model.inco.keep_stride_count


def temporal_only_conv(module, name, removed, stride_removed=0):
    """

    Recursively put desired batch norm in nn.module module.



    set module = net to start code.

    """
    # go through all attributes of module nn.module (e.g. network or layer) and put batch norms if present
    for attr_str in dir(module):
        sub_module = getattr(module, attr_str)
        if type(sub_module) == nn.Conv3d:
            target_spatial_size = 1
            predefine_padding = {1: 0, 3: 1, 5: 2, 7: 3}
            kernel_size = list(sub_module.kernel_size)
            assert kernel_size[1] == kernel_size[2]
            stride = sub_module.stride
            extra = None
            if stride[1] == stride[2] == 2:
                stride_removed += 1
                if stride_removed > keep_stride_count:
                    stride = [1, 1, 1]
                    extra = nn.MaxPool3d((1, 2, 2))

            if kernel_size[1] == 1 and extra is None:
                continue
            padding = list(sub_module.padding)

            kernel_size[1] = kernel_size[2] = target_spatial_size
            padding[1] = padding[2] = predefine_padding[target_spatial_size]
            if 'device' in parameters:
                parameters.remove('device')
            if 'dtype' in parameters:
                parameters.remove('dtype')
            param_dict = {key: getattr(sub_module, key) for key in parameters}

            param_dict.update(kernel_size=kernel_size, padding=padding, stride=stride)

            conv = nn.Conv3d(**param_dict)

            new_module = conv

            removed += 1
            if removed > spatial_count:
                setattr(module, attr_str, new_module)
                if extra is not None:
                    if attr_str == "conv":
                        bn_str = "bn"
                    else:
                        bn_str = f"{attr_str}_bn"
                    bn_module = getattr(module, bn_str)
                    assert isinstance(bn_module, nn.BatchNorm3d)
                    new_bn_module = nn.Sequential(bn_module, extra)
                    setattr(module, bn_str, new_bn_module)
            else:
                print("keep spatial")
        elif type(sub_module) == nn.Dropout:
            new_module = nn.Dropout(p=0.5)
            setattr(module, attr_str, new_module)
        if my_cfg.model.inco.no_time_pool:
            if type(sub_module) == nn.MaxPool3d:
                kernel_size = list(sub_module.kernel_size)
                if kernel_size[0] == 2:
                    kernel_size[0] = 1
                    setattr(module, attr_str, nn.MaxPool3d(kernel_size))
            elif type(sub_module) == nn.AvgPool3d:
                kernel_size = list(sub_module.kernel_size)
                kernel_size[0] = 2 * kernel_size[0]
                setattr(module, attr_str, nn.AvgPool3d(kernel_size))

    # iterate through immediate child modules. Note, the recursion is done by our code no need to use named_modules()
    old_name = name
    for name, immediate_child_module in module.named_children():
        removed, stride_removed = temporal_only_conv(
            immediate_child_module, old_name + "." + name, removed, stride_removed
        )
    return removed, stride_removed


class I3D8x8(nn.Module):
    def __init__(self) -> None:
        super(I3D8x8, self).__init__()
        cfg = get_cfg()
        cfg.merge_from_str(config_text)
        cfg.NUM_GPUS = 1
        cfg.TEST.BATCH_SIZE = 1
        cfg.TRAIN.BATCH_SIZE = 1

        cfg.DATA.NUM_FRAMES = my_cfg.clip_size
        SOLVER = my_cfg.model.inco.SOLVER
        if SOLVER is not None:
            for key, val in SOLVER.to_dict().items():
                old_val = getattr(cfg.SOLVER, key)
                val = type(old_val)(val)
                setattr(cfg.SOLVER, key, val)

        if my_cfg.model.inco.i3d_routine:
            self.cfg = cfg
        self.resnet = ResNetOri(cfg)
        temporal_only_conv(self.resnet, "model", 0)

        stop_point = my_cfg.model.transformer.stop_point
        
        for i in [5, 4, 3]:
            if stop_point <= i:
                setattr(self.resnet, f"s{i}", nn.Identity())
                if stop_point == 3:
                    setattr(self.resnet, f"pathway0_pool", nn.Identity())
        gc.collect()
        torch.cuda.empty_cache()

    def forward(

            self,

            images, ft_features=None,

            freeze_backbone=False

    ):
        assert not freeze_backbone

        inputs = [images]
        pred = self.resnet(inputs, ft_features)
        return pred