{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":104308,"databundleVersionId":12660285,"sourceType":"competition"},{"sourceId":444916,"sourceType":"modelInstanceVersion","modelInstanceId":361234,"modelId":382294}],"dockerImageVersionId":31040,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install timm","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-06-23T11:25:10.895750Z","iopub.execute_input":"2025-06-23T11:25:10.896515Z","iopub.status.idle":"2025-06-23T11:25:14.055271Z","shell.execute_reply.started":"2025-06-23T11:25:10.896488Z","shell.execute_reply":"2025-06-23T11:25:14.054345Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nfrom PIL import Image, UnidentifiedImageError\nimport cv2\nimport timm\nimport torchvision.transforms as T\nimport pandas as pd\nfrom pathlib import Path\nfrom tqdm import tqdm\n\nfrom functools import partial\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom timm.models.layers import trunc_normal_, DropPath\nfrom timm.models.registry import register_model\nimport torch.fft","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-23T11:25:14.056989Z","iopub.execute_input":"2025-06-23T11:25:14.057593Z","iopub.status.idle":"2025-06-23T11:25:14.062211Z","shell.execute_reply.started":"2025-06-23T11:25:14.057568Z","shell.execute_reply":"2025-06-23T11:25:14.061523Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Copyright (c) Meta Platforms, Inc. and affiliates.\n\n# All rights reserved.\n\n# This source code is licensed under the license found in the\n# LICENSE file in the root directory of this source tree.\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom timm.models.layers import trunc_normal_, DropPath\n#import numpy.random as random\n\nclass LayerNorm(nn.Module):\n    \"\"\" LayerNorm that supports two data formats: channels_last (default) or channels_first. \n    The ordering of the dimensions in the inputs. channels_last corresponds to inputs with \n    shape (batch_size, height, width, channels) while channels_first corresponds to inputs \n    with shape (batch_size, channels, height, width).\n    \"\"\"\n    def __init__(self, normalized_shape, eps=1e-6, data_format=\"channels_last\"):\n        super().__init__()\n        self.weight = nn.Parameter(torch.ones(normalized_shape))\n        self.bias = nn.Parameter(torch.zeros(normalized_shape))\n        self.eps = eps\n        self.data_format = data_format\n        if self.data_format not in [\"channels_last\", \"channels_first\"]:\n            raise NotImplementedError \n        self.normalized_shape = (normalized_shape, )\n    \n    def forward(self, x):\n        if self.data_format == \"channels_last\":\n            return F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps)\n        elif self.data_format == \"channels_first\":\n            u = x.mean(1, keepdim=True)\n            s = (x - u).pow(2).mean(1, keepdim=True)\n            x = (x - u) / torch.sqrt(s + self.eps)\n            x = self.weight[:, None, None] * x + self.bias[:, None, None]\n            return x\n\nclass GRN(nn.Module):\n    \"\"\" GRN (Global Response Normalization) layer\n    \"\"\"\n    def __init__(self, dim):\n        super().__init__()\n        self.gamma = nn.Parameter(torch.zeros(1, 1, 1, dim))\n        self.beta = nn.Parameter(torch.zeros(1, 1, 1, dim))\n\n    def forward(self, x):\n        Gx = torch.norm(x, p=2, dim=(1,2), keepdim=True)\n        Nx = Gx / (Gx.mean(dim=-1, keepdim=True) + 1e-6)\n        return self.gamma * (x * Nx) + self.beta + x\n\n \nclass Block(nn.Module):\n    \"\"\" ConvNeXtV2 Block.\n    \n    Args:\n        dim (int): Number of input channels.\n        drop_path (float): Stochastic depth rate. Default: 0.0\n    \"\"\"\n    def __init__(self, dim, drop_path=0.):\n        super().__init__()\n        self.dwconv = nn.Conv2d(dim, dim, kernel_size=7, padding=3, groups=dim) # depthwise conv\n        self.norm = LayerNorm(dim, eps=1e-6)\n        self.pwconv1 = nn.Linear(dim, 4 * dim) # pointwise/1x1 convs, implemented with linear layers\n        self.act = nn.GELU()\n        self.grn = GRN(4 * dim)\n        self.pwconv2 = nn.Linear(4 * dim, dim)\n        self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()\n\n    def forward(self, x):\n        input = x\n        x = self.dwconv(x)\n        x = x.permute(0, 2, 3, 1) # (N, C, H, W) -> (N, H, W, C)\n        x = self.norm(x)\n        x = self.pwconv1(x)\n        x = self.act(x)\n        x = self.grn(x)\n        x = self.pwconv2(x)\n        x = x.permute(0, 3, 1, 2) # (N, H, W, C) -> (N, C, H, W)\n\n        x = input + self.drop_path(x)\n        return x\n\nclass ConvNeXtV2(nn.Module):\n    \"\"\" ConvNeXt V2\n        \n    Args:\n        in_chans (int): Number of input image channels. Default: 3\n        num_classes (int): Number of classes for classification head. Default: 1000\n        depths (tuple(int)): Number of blocks at each stage. Default: [3, 3, 9, 3]\n        dims (int): Feature dimension at each stage. Default: [96, 192, 384, 768]\n        drop_path_rate (float): Stochastic depth rate. Default: 0.\n        head_init_scale (float): Init scaling value for classifier weights and biases. Default: 1.\n    \"\"\"\n    def __init__(self, in_chans=3, num_classes=1000, \n                 depths=[3, 3, 9, 3], dims=[96, 192, 384, 768], \n                 drop_path_rate=0., head_init_scale=1., \n                 ):\n        super().__init__()\n        self.depths = depths\n        self.downsample_layers = nn.ModuleList() # stem and 3 intermediate downsampling conv layers\n        stem = nn.Sequential(\n            nn.Conv2d(in_chans, dims[0], kernel_size=4, stride=4),\n            LayerNorm(dims[0], eps=1e-6, data_format=\"channels_first\")\n        )\n        self.downsample_layers.append(stem)\n        for i in range(3):\n            downsample_layer = nn.Sequential(\n                    LayerNorm(dims[i], eps=1e-6, data_format=\"channels_first\"),\n                    nn.Conv2d(dims[i], dims[i+1], kernel_size=2, stride=2),\n            )\n            self.downsample_layers.append(downsample_layer)\n\n        self.stages = nn.ModuleList() # 4 feature resolution stages, each consisting of multiple residual blocks\n        dp_rates=[x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))] \n        cur = 0\n        # for i in range(4):\n        #     stage = nn.Sequential(\n        #         *[Block(dim=dims[i], drop_path=dp_rates[cur + j]) for j in range(depths[i])]\n        #     )\n        for i in range(4):\n            stage = nn.Sequential(\n                *[Block(dim=dims[i],\n                        drop_path=dp_rates[cur + j],\n                        ) for j in range(depths[i])]\n            )\n            self.stages.append(stage)\n            cur += depths[i]\n\n        self.norm = nn.LayerNorm(dims[-1], eps=1e-6) # final norm layer\n        self.head = nn.Linear(dims[-1], num_classes)\n\n        self.apply(self._init_weights)\n        self.head.weight.data.mul_(head_init_scale)\n        self.head.bias.data.mul_(head_init_scale)\n\n    def _init_weights(self, m):\n        if isinstance(m, (nn.Conv2d, nn.Linear)):\n            trunc_normal_(m.weight, std=.02)\n            if m.bias is not None:  # 수정한 부분\n                nn.init.constant_(m.bias, 0)\n\n    def forward_features(self, x):\n        for i in range(4):\n            x = self.downsample_layers[i](x)\n            x = self.stages[i](x)\n        return self.norm(x.mean([-2, -1])) # global average pooling, (N, C, H, W) -> (N, C)\n\n    def forward(self, x):\n        x = self.forward_features(x)\n        x = self.head(x)\n        return x\n\ndef convnextv2_atto(**kwargs):\n    model = ConvNeXtV2(depths=[2, 2, 6, 2], dims=[40, 80, 160, 320], **kwargs)\n    return model\n\ndef convnextv2_femto(**kwargs):\n    model = ConvNeXtV2(depths=[2, 2, 6, 2], dims=[48, 96, 192, 384], **kwargs)\n    return model\n\ndef convnext_pico(**kwargs):\n    model = ConvNeXtV2(depths=[2, 2, 6, 2], dims=[64, 128, 256, 512], **kwargs)\n    return model\n\ndef convnextv2_nano(**kwargs):\n    model = ConvNeXtV2(depths=[2, 2, 8, 2], dims=[80, 160, 320, 640], **kwargs)\n    return model\n\ndef convnextv2_tiny(**kwargs):\n    model = ConvNeXtV2(depths=[3, 3, 9, 3], dims=[96, 192, 384, 768], **kwargs)\n    return model\n\ndef convnextv2_base(**kwargs):\n    model = ConvNeXtV2(depths=[3, 3, 27, 3], dims=[128, 256, 512, 1024], **kwargs)\n    return model\n\ndef convnextv2_large(**kwargs):\n    model = ConvNeXtV2(depths=[3, 3, 27, 3], dims=[192, 384, 768, 1536], **kwargs)\n    return model\n\ndef convnextv2_huge(**kwargs):\n    model = ConvNeXtV2(depths=[3, 3, 27, 3], dims=[352, 704, 1408, 2816], **kwargs)\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-23T11:25:14.063035Z","iopub.execute_input":"2025-06-23T11:25:14.063286Z","iopub.status.idle":"2025-06-23T11:25:14.087993Z","shell.execute_reply.started":"2025-06-23T11:25:14.063269Z","shell.execute_reply":"2025-06-23T11:25:14.087479Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_dwconv(dim, kernel, bias):\n    return nn.Conv2d(dim, dim, kernel_size=kernel, padding=(kernel-1)//2 ,bias=bias, groups=dim)\n\nclass GlobalLocalFilter(nn.Module):\n    def __init__(self, dim, h=14, w=8):\n        super().__init__()\n        self.dw = nn.Conv2d(dim // 2, dim // 2, kernel_size=3, padding=1, bias=False, groups=dim // 2)\n        self.complex_weight = nn.Parameter(torch.randn(dim // 2, h, w, 2, dtype=torch.float32) * 0.02)\n        trunc_normal_(self.complex_weight, std=.02)\n        self.pre_norm = LayerNorm(dim, eps=1e-6, data_format='channels_first')\n        self.post_norm = LayerNorm(dim, eps=1e-6, data_format='channels_first')\n\n    def forward(self, x):\n        x = self.pre_norm(x)\n        x1, x2 = torch.chunk(x, 2, dim=1)\n        x1 = self.dw(x1)\n\n        x2 = x2.to(torch.float32)\n        B, C, a, b = x2.shape\n        x2 = torch.fft.rfft2(x2, dim=(2, 3), norm='ortho')\n\n        weight = self.complex_weight\n        if not weight.shape[1:3] == x2.shape[2:4]:\n            weight = F.interpolate(weight.permute(3,0,1,2), size=x2.shape[2:4], mode='bilinear', align_corners=True).permute(1,2,3,0)\n\n        weight = torch.view_as_complex(weight.contiguous())\n\n        x2 = x2 * weight\n        x2 = torch.fft.irfft2(x2, s=(a, b), dim=(2, 3), norm='ortho')\n\n        x = torch.cat([x1.unsqueeze(2), x2.unsqueeze(2)], dim=2).reshape(B, 2 * C, a, b)\n        x = self.post_norm(x)\n        return x\n\n\nclass gnconv(nn.Module):\n    def __init__(self, dim, order=5, gflayer=None, h=14, w=8, s=1.0):\n        super().__init__()\n        self.order = order\n        self.dims = [dim // 2 ** i for i in range(order)]\n        self.dims.reverse()\n        self.proj_in = nn.Conv2d(dim, 2*dim, 1)\n\n        if gflayer is None:\n            self.dwconv = get_dwconv(sum(self.dims), 7, True)\n        else:\n            self.dwconv = gflayer(sum(self.dims), h=h, w=w)\n        \n        self.proj_out = nn.Conv2d(dim, dim, 1)\n\n        self.pws = nn.ModuleList(\n            [nn.Conv2d(self.dims[i], self.dims[i+1], 1) for i in range(order-1)]\n        )\n\n        self.scale = s\n        # print('[gnconv]', order, 'order with dims=', self.dims, 'scale=%.4f'%self.scale)\n\n    def forward(self, x, mask=None, dummy=False):\n        B, C, H, W = x.shape\n\n        fused_x = self.proj_in(x)\n        pwa, abc = torch.split(fused_x, (self.dims[0], sum(self.dims)), dim=1)\n\n        dw_abc = self.dwconv(abc) * self.scale\n\n        dw_list = torch.split(dw_abc, self.dims, dim=1)\n        x = pwa * dw_list[0]\n\n        for i in range(self.order -1):\n            x = self.pws[i](x) * dw_list[i+1]\n\n        x = self.proj_out(x)\n\n        return x\n\nclass Block(nn.Module):\n    r\"\"\" HorNet block\n    \"\"\"\n    def __init__(self, dim, drop_path=0., layer_scale_init_value=1e-6, gnconv=gnconv):\n        super().__init__()\n\n        self.norm1 = LayerNorm(dim, eps=1e-6, data_format='channels_first')\n        self.gnconv = gnconv(dim) # depthwise conv\n        self.norm2 = LayerNorm(dim, eps=1e-6)\n        self.pwconv1 = nn.Linear(dim, 4 * dim) # pointwise/1x1 convs, implemented with linear layers\n        self.act = nn.GELU()\n        self.pwconv2 = nn.Linear(4 * dim, dim)\n\n        self.gamma1 = nn.Parameter(layer_scale_init_value * torch.ones(dim), \n                                    requires_grad=True) if layer_scale_init_value > 0 else None\n\n        self.gamma2 = nn.Parameter(layer_scale_init_value * torch.ones((dim)), \n                                    requires_grad=True) if layer_scale_init_value > 0 else None\n        self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()\n\n    def forward(self, x):\n        B, C, H, W  = x.shape\n        if self.gamma1 is not None:\n            gamma1 = self.gamma1.view(C, 1, 1)\n        else:\n            gamma1 = 1\n        x = x + self.drop_path(gamma1 * self.gnconv(self.norm1(x)))\n\n        input = x\n        x = x.permute(0, 2, 3, 1) # (N, C, H, W) -> (N, H, W, C)\n        x = self.norm2(x)\n        x = self.pwconv1(x)\n        x = self.act(x)\n        x = self.pwconv2(x)\n        if self.gamma2 is not None:\n            x = self.gamma2 * x\n        x = x.permute(0, 3, 1, 2) # (N, H, W, C) -> (N, C, H, W)\n\n        x = input + self.drop_path(x)\n        return x\n\n\nclass HorNet(nn.Module):\n    def __init__(self, in_chans=3, num_classes=2, \n                 depths=[3, 3, 9, 3], base_dim=96, drop_path_rate=0.,\n                 layer_scale_init_value=1e-6, head_init_scale=1.,\n                 gnconv=gnconv, block=Block, uniform_init=False, **kwargs\n                 ):\n        super().__init__()\n        dims = [base_dim, base_dim*2, base_dim*4, base_dim*8]\n\n        self.downsample_layers = nn.ModuleList() # stem and 3 intermediate downsampling conv layers\n        stem = nn.Sequential(\n            nn.Conv2d(in_chans, dims[0], kernel_size=4, stride=4),\n            LayerNorm(dims[0], eps=1e-6, data_format=\"channels_first\")\n        )\n        self.downsample_layers.append(stem)\n        for i in range(3):\n            downsample_layer = nn.Sequential(\n                    LayerNorm(dims[i], eps=1e-6, data_format=\"channels_first\"),\n                    nn.Conv2d(dims[i], dims[i+1], kernel_size=2, stride=2),\n            )\n            self.downsample_layers.append(downsample_layer)\n\n        self.stages = nn.ModuleList() # 4 feature resolution stages, each consisting of multiple residual blocks\n        dp_rates=[x.item() for x in torch.linspace(0, drop_path_rate, sum(depths))] \n\n\n        if not isinstance(gnconv, list):\n            gnconv = [gnconv, gnconv, gnconv, gnconv]\n        else:\n            gnconv = gnconv\n            assert len(gnconv) == 4\n\n        cur = 0\n        for i in range(4):\n            stage = nn.Sequential(\n                *[block(dim=dims[i], drop_path=dp_rates[cur + j], \n                layer_scale_init_value=layer_scale_init_value, gnconv=gnconv[i]) for j in range(depths[i])]\n            )\n            self.stages.append(stage)\n            cur += depths[i]\n\n        self.norm = nn.LayerNorm(dims[-1], eps=1e-6) # final norm layer\n        self.head = nn.Linear(dims[-1], num_classes)\n\n        self.uniform_init = uniform_init\n\n        self.apply(self._init_weights)\n        self.head.weight.data.mul_(head_init_scale)\n        self.head.bias.data.mul_(head_init_scale)\n\n    def _init_weights(self, m):\n        if not self.uniform_init:\n            if isinstance(m, (nn.Conv2d, nn.Linear)):\n                trunc_normal_(m.weight, std=.02)\n                if hasattr(m, 'bias') and m.bias is not None:\n                    nn.init.constant_(m.bias, 0)\n        else:\n            if isinstance(m, (nn.Conv2d, nn.Linear)):\n                nn.init.xavier_uniform_(m.weight)\n                if hasattr(m, 'bias') and m.bias is not None:\n                    nn.init.constant_(m.bias, 0)            \n\n    def forward_features(self, x):\n        for i in range(4):\n            x = self.downsample_layers[i](x)\n            for j, blk in enumerate(self.stages[i]):\n                    x = blk(x)\n        return self.norm(x.mean([-2, -1])) # global average pooling, (N, C, H, W) -> (N, C)\n\n    def forward(self, x):\n        x = self.forward_features(x)\n        x = self.head(x)\n        return x\n\nclass LayerNorm(nn.Module):\n    r\"\"\" LayerNorm that supports two data formats: channels_last (default) or channels_first. \n    The ordering of the dimensions in the inputs. channels_last corresponds to inputs with \n    shape (batch_size, height, width, channels) while channels_first corresponds to inputs \n    with shape (batch_size, channels, height, width).\n    \"\"\"\n    def __init__(self, normalized_shape, eps=1e-6, data_format=\"channels_last\"):\n        super().__init__()\n        self.weight = nn.Parameter(torch.ones(normalized_shape))\n        self.bias = nn.Parameter(torch.zeros(normalized_shape))\n        self.eps = eps\n        self.data_format = data_format\n        if self.data_format not in [\"channels_last\", \"channels_first\"]:\n            raise NotImplementedError \n        self.normalized_shape = (normalized_shape, )\n    \n    def forward(self, x):\n        if self.data_format == \"channels_last\":\n            return F.layer_norm(x, self.normalized_shape, self.weight, self.bias, self.eps)\n        elif self.data_format == \"channels_first\":\n            u = x.mean(1, keepdim=True)\n            s = (x - u).pow(2).mean(1, keepdim=True)\n            x = (x - u) / torch.sqrt(s + self.eps)\n            x = self.weight[:, None, None] * x + self.bias[:, None, None]\n            return x\n\ndef hornet_tiny_7x7(pretrained=False,in_22k=False, **kwargs):\n    s = 1.0/3.0\n    model = HorNet(depths=[2, 3, 18, 2], base_dim=64, block=Block,\n    gnconv=[\n        partial(gnconv, order=2, s=s),\n        partial(gnconv, order=3, s=s),\n        partial(gnconv, order=4, s=s),\n        partial(gnconv, order=5, s=s),\n    ],\n    **kwargs\n    )\n    return model\n\ndef hornet_tiny_gf(pretrained=False,in_22k=False, num_classes=2, **kwargs):\n    s = 1.0/3.0\n    model = HorNet(num_classes=num_classes, depths=[2, 3, 18, 2], base_dim=64, block=Block,\n    gnconv=[\n        partial(gnconv, order=2, s=s),\n        partial(gnconv, order=3, s=s),\n        partial(gnconv, order=4, s=s, h=14, w=8, gflayer=GlobalLocalFilter),\n        partial(gnconv, order=5, s=s, h=7, w=4, gflayer=GlobalLocalFilter),\n    ],\n    **kwargs\n    )\n    return model\n\ndef hornet_small_7x7(pretrained=False,in_22k=False, **kwargs):\n    s = 1.0/3.0\n    model = HorNet(depths=[2, 3, 18, 2], base_dim=96, block=Block,\n    gnconv=[\n        partial(gnconv, order=2, s=s),\n        partial(gnconv, order=3, s=s),\n        partial(gnconv, order=4, s=s),\n        partial(gnconv, order=5, s=s),\n    ],\n    **kwargs\n    )\n    return model\n\ndef hornet_small_gf(pretrained=False,in_22k=False, **kwargs):\n    s = 1.0/3.0\n    model = HorNet(depths=[2, 3, 18, 2], base_dim=96, block=Block,\n    gnconv=[\n        partial(gnconv, order=2, s=s),\n        partial(gnconv, order=3, s=s),\n        partial(gnconv, order=4, s=s, h=14, w=8, gflayer=GlobalLocalFilter),\n        partial(gnconv, order=5, s=s, h=7, w=4, gflayer=GlobalLocalFilter),\n    ],\n    **kwargs\n    )\n    return model\n\ndef hornet_base_7x7(pretrained=False,in_22k=False, **kwargs):\n    s = 1.0/3.0\n    model = HorNet(depths=[2, 3, 18, 2], base_dim=128, block=Block,\n    gnconv=[\n        partial(gnconv, order=2, s=s),\n        partial(gnconv, order=3, s=s),\n        partial(gnconv, order=4, s=s),\n        partial(gnconv, order=5, s=s),\n    ],\n    **kwargs\n    )\n    return model\n\ndef hornet_base_gf(pretrained=False,in_22k=False, **kwargs):\n    s = 1.0/3.0\n    model = HorNet(depths=[2, 3, 18, 2], base_dim=128, block=Block,\n    gnconv=[\n        partial(gnconv, order=2, s=s),\n        partial(gnconv, order=3, s=s),\n        partial(gnconv, order=4, s=s, h=14, w=8, gflayer=GlobalLocalFilter),\n        partial(gnconv, order=5, s=s, h=7, w=4, gflayer=GlobalLocalFilter),\n    ],\n    **kwargs\n    )\n    return model\n\ndef hornet_base_gf_img384(pretrained=False,in_22k=False, **kwargs):\n    s = 1.0/3.0\n    model = HorNet(depths=[2, 3, 18, 2], base_dim=128, block=Block,\n    gnconv=[\n        partial(gnconv, order=2, s=s),\n        partial(gnconv, order=3, s=s),\n        partial(gnconv, order=4, s=s, h=24, w=13, gflayer=GlobalLocalFilter),\n        partial(gnconv, order=5, s=s, h=12, w=7, gflayer=GlobalLocalFilter),\n    ],\n    **kwargs\n    )\n    return model\n\ndef hornet_large_7x7(pretrained=False,in_22k=False, **kwargs):\n    s = 1.0/3.0\n    model = HorNet(depths=[2, 3, 18, 2], base_dim=192, block=Block,\n    gnconv=[\n        partial(gnconv, order=2, s=s),\n        partial(gnconv, order=3, s=s),\n        partial(gnconv, order=4, s=s),\n        partial(gnconv, order=5, s=s),\n    ],\n    **kwargs\n    )\n    return model\n\ndef hornet_large_gf(pretrained=False,in_22k=False, num_classes=2, **kwargs):\n    s = 1.0/3.0\n    model = HorNet(num_classes=num_classes, depths=[2, 3, 18, 2], base_dim=192, block=Block,\n    gnconv=[\n        partial(gnconv, order=2, s=s),\n        partial(gnconv, order=3, s=s),\n        partial(gnconv, order=4, s=s, h=14, w=8, gflayer=GlobalLocalFilter),\n        partial(gnconv, order=5, s=s, h=7, w=4, gflayer=GlobalLocalFilter),\n    ],\n    **kwargs\n    )\n    return model\n\ndef hornet_large_gf_img384(pretrained=False,in_22k=False, num_classes=2, **kwargs):\n    s = 1.0/3.0\n    model = HorNet(depths=[2, 3, 18, 2], base_dim=192, block=Block,\n    gnconv=[\n        partial(gnconv, order=2, s=s),\n        partial(gnconv, order=3, s=s),\n        partial(gnconv, order=4, s=s, h=24, w=13, gflayer=GlobalLocalFilter),\n        partial(gnconv, order=5, s=s, h=12, w=7, gflayer=GlobalLocalFilter),\n    ],\n    **kwargs\n    )\n    return model\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-23T11:25:14.089388Z","iopub.execute_input":"2025-06-23T11:25:14.089581Z","iopub.status.idle":"2025-06-23T11:25:14.128845Z","shell.execute_reply.started":"2025-06-23T11:25:14.089567Z","shell.execute_reply":"2025-06-23T11:25:14.128306Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"DEVICE    = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\nTEST_DIR  = Path(\"/kaggle/input/Deepfake_Detection_and_Generation_Challenge_Blue_Team/test_sample_videos\")\nOUT_CSV   = Path(\"/kaggle/working/submission.csv\")\n\n# 체크포인트 경로\nRGB_CKPT  = Path(\"/kaggle/input/deepfake_ensemble_model_weight/pytorch/convnextv2_dct_fft_hornet/1/hornet_base_ddp_best.pth\")\nFREQ_CKPT = Path(\"/kaggle/input/deepfake_ensemble_model_weight/pytorch/convnextv2_dct_fft_hornet/1/convnext_fft_1_best.pth\")\nDCT_CKPT  = Path(\"/kaggle/input/deepfake_ensemble_model_weight/pytorch/convnextv2_dct_fft_hornet/1/convnext_best.pth\")\nMLP_CKPT  = Path(\"/kaggle/input/deepfake_ensemble_model_weight/pytorch/convnextv2_dct_fft_hornet/1/mlp_fusion_3model_best.pth\")\n\n# ─── FFT/DCT 추출 ───────────────────────────────────────\ndef extract_fft(bgr):\n    chans = []\n    for ch in cv2.split(bgr):\n        dft   = cv2.dft(ch.astype(np.float32), flags=cv2.DFT_COMPLEX_OUTPUT)\n        shift = np.fft.fftshift(dft)\n        mag   = cv2.magnitude(shift[:,:,0], shift[:,:,1])\n        chans.append((20*np.log(mag+1)).astype(np.float32))\n    return np.stack(chans,2)\n\ndef extract_dct(bgr):\n    gray = cv2.cvtColor(bgr, cv2.COLOR_BGR2GRAY).astype(np.float32)/255.0\n    return cv2.dct(gray).astype(np.float32)\n\n# ─── 베이스 모델 로드 ────────────────────────────────────\ndef load_base(kind, ckpt):\n    sd = torch.load(ckpt, map_location=DEVICE)\n    sd = {k.replace('model.',''):v for k,v in sd.items()}\n\n    if kind=='hornet':\n        m = hornet_base_gf(num_classes=2)\n    elif kind in ('fft','convnext'):\n        # FFT용 ConvNeXtV2\n        stem = next(v for k,v in sd.items() if k.endswith('downsample_layers.0.0.weight'))\n        m = convnextv2_large(in_chans=stem.shape[1], num_classes=2)\n    elif kind=='dct':\n        stem = next(v for k,v in sd.items() if k.endswith('downsample_layers.0.0.weight'))\n        m = ConvNeXtV2(in_chans=stem.shape[1], num_classes=2)\n    else:\n        raise ValueError\n    m.load_state_dict(sd, strict=False)\n    return m.to(DEVICE).eval()\n\n# ─── MLP 정의 ────────────────────────────────────────────\nclass MetaMLP(nn.Module):\n    def __init__(self, in_dim=6):\n        super().__init__()\n        self.net = nn.Sequential(\n            nn.Linear(in_dim,128), nn.ReLU(),\n            nn.Linear(128,2)\n        )\n    def forward(self,x): return self.net(x)\n\n# ─── Confidence‐튜닝 ─────────────────────────────────────\ndef tune_confidence(probs):\n    fake = probs[:,1]\n    conf = 2*np.abs(fake-0.5)\n    return np.where(fake<0.5,\n                    fake*conf + 0.5*(1-conf),\n                    fake)\n\n# ─── 전처리 ──────────────────────────────────────────────\nresize  = T.Resize((224,224))\nto_tensor = T.Compose([\n    T.ToTensor(),\n    T.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])\n])\nsoftmax = nn.Softmax(dim=1)\n\n# ─── 프레임 샘플링 & 추론 ─────────────────────────────────\ndef video_predict(vp, sample_rate=10):\n    cap = cv2.VideoCapture(str(vp))\n    idx, scores = 0, []\n    while True:\n        ret, frame = cap.read()\n        if not ret: break\n        if idx % sample_rate == 0:\n            # RGB→PIL\n            img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)\n            pil = Image.fromarray(img)\n            pil = resize(pil)\n            # 1) RGB\n            inp = to_tensor(pil).unsqueeze(0).to(DEVICE)\n            with torch.no_grad():\n                p_r = softmax(rgb_model(inp))[0].cpu().numpy()\n            torch.cuda.empty_cache()\n            # 2) FFT\n            bgr = np.array(pil)[:,:,::-1]\n            ffft= extract_fft(bgr)\n            inp2= torch.from_numpy(\n                    np.concatenate([bgr,ffft],2)\n                    .transpose(2,0,1)[None]\n                 ).to(DEVICE)\n            with torch.no_grad():\n                p_f = softmax(freq_model(inp2))[0].cpu().numpy()\n            torch.cuda.empty_cache()\n            # 3) DCT\n            \n            bgr2= (np.array(pil)[:,:,::-1]*255).astype(np.uint8)\n            dctm= extract_dct(bgr2)\n            four= np.stack([dctm]*4,2)\n            inp3= torch.from_numpy(four.transpose(2,0,1)[None]).to(DEVICE)\n            with torch.no_grad():\n                p_d = softmax(dct_model(inp3))[0].cpu().numpy()\n            torch.cuda.empty_cache()\n            # 4) MLP‐Fusion\n            x = torch.from_numpy(np.concatenate([p_r,p_f,p_d])).float().to(DEVICE)\n            with torch.no_grad():\n                p_m = softmax(mlp(x.unsqueeze(0)))[0].cpu().numpy()\n            # 5) Confidence‐튜닝\n            score = tune_confidence(p_m[np.newaxis,:])[0]\n            scores.append(score)\n        idx += 1\n    cap.release()\n    return float(np.mean(scores)) if scores else 0.0\n\n# ─── 메인 ────────────────────────────────────────────────\nif __name__=='__main__':\n    # 1) 모델 load\n    rgb_model  = load_base('hornet',   RGB_CKPT)\n    freq_model = load_base('fft',      FREQ_CKPT)\n    dct_model  = load_base('dct',      DCT_CKPT)\n    mlp        = MetaMLP(in_dim=6).to(DEVICE)\n    mlp.load_state_dict(torch.load(MLP_CKPT, map_location=DEVICE))\n    mlp.eval()\n\n    # 2) 모든 비디오에 대해\n    video_paths = sorted(TEST_DIR.glob('**/*.mp4'))\n    results = []\n    for vp in tqdm(video_paths, desc=\"Processing videos\"):\n        score = video_predict(vp, sample_rate=10)\n        label = 1 if score>0.5 else 0\n        vid   = str(vp.relative_to(TEST_DIR))\n        results.append((vid,label))\n\n    # 3) CSV\n    pd.DataFrame(results, columns=['ID','label']) \\\n      .to_csv(OUT_CSV, index=False)\n    print(\"✅ saved to\", OUT_CSV)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-06-23T11:25:14.129536Z","iopub.execute_input":"2025-06-23T11:25:14.129723Z","iopub.status.idle":"2025-06-23T11:59:05.944001Z","shell.execute_reply.started":"2025-06-23T11:25:14.129703Z","shell.execute_reply":"2025-06-23T11:59:05.943362Z"}},"outputs":[],"execution_count":null}]}