{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install segmentation-models-pytorch","metadata":{"execution":{"iopub.status.busy":"2022-08-18T12:57:46.652183Z","iopub.execute_input":"2022-08-18T12:57:46.652748Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 导入库","metadata":{}},{"cell_type":"code","source":"import sys\nsys.path.append('../input/timm-pytorch-image-models/pytorch-image-models-master')","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import gc\nimport os\nimport random\nimport time\nimport warnings\nimport sys\nimport glob\nfrom torch.optim import lr_scheduler\nimport copy\nfrom collections import defaultdict\nfrom torch.cuda import amp\n\nwarnings.simplefilter(\"ignore\")\nimport timm\nimport albumentations as A\n# from albumentations.pytorch import ToTensor\nimport cv2\nfrom matplotlib import pyplot as plt\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image, ImageFilter\nfrom sklearn.model_selection import KFold\nimport segmentation_models_pytorch as smp\nimport torch\nimport torch.backends.cudnn as cudnn\nimport torch.nn as nn\nfrom torch.optim import AdamW, SGD\nfrom torch.nn import functional as F\nimport torch.optim as optim\nfrom torch.optim.lr_scheduler import ReduceLROnPlateau\nfrom torch.utils.data import DataLoader, Dataset, sampler\nfrom tqdm import tqdm\nfrom torch.optim.lr_scheduler import CosineAnnealingLR, CosineAnnealingWarmRestarts, StepLR, ReduceLROnPlateau\nfrom torch.cuda.amp import autocast, GradScaler\nfrom torchvision import transforms\nfrom sklearn.model_selection import StratifiedKFold, GroupKFold, KFold","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 固定随机种子","metadata":{}},{"cell_type":"code","source":"def set_seed(seed=2 ** 3):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class CFG:\n    seed          = 101\n    debug         =  True # \n    exp_name      = 'Baselinev1'\n    train_bs      = 8\n    valid_bs      = train_bs*2\n    img_size      = [384, 384]\n    epochs        = 5\n    lr            = 1e-4\n    scheduler     = 'CosineAnnealingLR'\n    min_lr        = 1e-6\n    T_max         = 10\n    T_0           = 25\n    warmup_epochs = 0\n    wd            = 1e-6\n    n_accumulate  = max(1, 32//train_bs)\n    n_fold        = 5\n    num_classes   = 3\n    device        = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 数据准备\ntrain_path = glob.glob('../input/boolart-cityscapes/data/train/*/*')\ndf_train = pd.DataFrame({\n    \"image_path\":train_path\n})\ndf_train['mask_path'] = df_train['image_path'].str.replace(\"data\",'label')\ndf_train['mask_path'] = df_train['mask_path'].apply(lambda x:x.split('.png')[0][:-11] + 'gtFine_labelTrainIds.png')\nFold = KFold(n_splits=5)\nfor n, (train_index, val_index) in enumerate(Fold.split(df_train)):\n    df_train.loc[val_index, 'fold'] = int(n)\ndf_train['fold'] = df_train['fold'].astype(int)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 数据加载","metadata":{}},{"cell_type":"code","source":"data_transforms = {\n    \"train\": A.Compose([\n        A.Resize(*CFG.img_size, interpolation=cv2.INTER_NEAREST),\n        A.HorizontalFlip(p=0.5),\n#         A.VerticalFlip(p=0.5),\n        A.ShiftScaleRotate(shift_limit=0.0625, scale_limit=0.05, rotate_limit=10, p=0.5),\n        A.OneOf([\n            A.GridDistortion(num_steps=5, distort_limit=0.05, p=1.0),\n#             A.OpticalDistortion(distort_limit=0.05, shift_limit=0.05, p=1.0),\n            A.ElasticTransform(alpha=1, sigma=50, alpha_affine=50, p=1.0)\n        ], p=0.25),\n        A.CoarseDropout(max_holes=8, max_height=CFG.img_size[0]//20, max_width=CFG.img_size[1]//20,\n                         min_holes=5, fill_value=0, mask_fill_value=0, p=0.5),\n        ], p=1.0),\n    \n    \"valid\": A.Compose([\n        A.Resize(*CFG.img_size, interpolation=cv2.INTER_NEAREST),\n        ], p=1.0)\n}\n\nclass BuildDataset(torch.utils.data.Dataset):\n    def __init__(self, df, label=True, transforms=None):\n        self.df         = df\n        self.label      = label\n        self.img_paths  = df['image_path'].tolist()\n        self.msk_paths  = df['mask_path'].tolist()\n        self.transforms = transforms\n        \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, index):\n        img_path  = self.img_paths[index]\n        img = cv2.imread(img_path)\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        msk_path = self.msk_paths[index]\n        msk = np.array(Image.open(msk_path))\n        if self.transforms:\n            data = self.transforms(image=img, mask=msk)\n            img  = data['image']\n            msk  = data['mask']\n        img = np.transpose(img, (2, 0, 1))\n        msk = torch.tensor(msk)\n        return torch.tensor(img), msk","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def prepare_loaders(fold, debug=False):\n    train_df = df_train.query(\"fold!=@fold\").reset_index(drop=True)\n    valid_df = df_train.query(\"fold==@fold\").reset_index(drop=True)\n    if debug:\n        train_df = train_df.head(32*15).reset_index(drop=True)\n        valid_df = valid_df.head(32*3).reset_index(drop=True)\n    train_dataset = BuildDataset(train_df, transforms=data_transforms['train'])\n    valid_dataset = BuildDataset(valid_df, transforms=data_transforms['valid'])\n\n    train_loader = DataLoader(train_dataset, batch_size=CFG.train_bs, \n                              num_workers=4, shuffle=True, pin_memory=True, drop_last=False)\n    valid_loader = DataLoader(valid_dataset, batch_size=CFG.valid_bs, \n                              num_workers=4, shuffle=False, pin_memory=True)\n    \n    return train_loader, valid_loader","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"增强后的数据可视化","metadata":{}},{"cell_type":"code","source":"train_loader, valid_loader = prepare_loaders(0)\nimgs,masks = next(iter(train_loader))\n\nplt.figure(figsize=(16,16))\nfor i,(img,mask) in enumerate(zip(imgs,masks)):\n    img = img.permute(1,2,0).numpy().astype(np.uint8)\n    plt.subplot(8,8,i+1)\n    plt.imshow(img,vmin=0,vmax=255)\n    plt.imshow(mask.squeeze().numpy(), alpha=0.2)\n    plt.axis('off')\n    plt.subplots_adjust(wspace=None, hspace=None)\n    \ndel train_loader,valid_loader,imgs,masks","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 模型\n","metadata":{}},{"cell_type":"code","source":"from timm.models.layers import DropPath, to_2tuple, trunc_normal_\n\n'''\ntodo:\ninitialisation\n'''\n##########################################################################################################\n\n\n# https://zhangtemplar.github.io/swin-transformer/\n# <no-change>\nclass Mlp(nn.Module):\n    def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.):\n        super().__init__()\n        out_features = out_features or in_features\n        hidden_features = hidden_features or in_features\n        self.fc1 = nn.Linear(in_features, hidden_features)\n        self.act = act_layer()\n        self.fc2 = nn.Linear(hidden_features, out_features)\n        self.drop = nn.Dropout(drop)\n\n    def forward(self, x):\n        x = self.fc1(x)\n        x = self.act(x)\n        x = self.drop(x)\n        x = self.fc2(x)\n        x = self.drop(x)\n        return x\n\n\n# <no-change>\ndef window_partition(x, window_size):\n    \"\"\"\n    Args:\n        x: (B, H, W, C)\n        window_size (int): window size\n\n    Returns:\n        windows: (num_windows*B, window_size, window_size, C)\n    \"\"\"\n    B, H, W, C = x.shape\n    x = x.view(B, H // window_size, window_size, W // window_size, window_size, C)\n    windows = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(-1, window_size, window_size, C)\n    return windows\n\n\n# <no-change>\ndef window_reverse(windows, window_size, H, W):\n    \"\"\"\n    Args:\n        windows: (num_windows*B, window_size, window_size, C)\n        window_size (int): Window size\n        H (int): Height of image\n        W (int): Width of image\n\n    Returns:\n        x: (B, H, W, C)\n    \"\"\"\n    B = int(windows.shape[0] / (H * W / window_size / window_size))\n    x = windows.view(B, H // window_size, W // window_size, window_size, window_size, -1)\n    x = x.permute(0, 1, 3, 2, 4, 5).contiguous().view(B, H, W, -1)\n    return x\n\n\n# <no-change>\nclass WindowAttention(nn.Module):\n    r\"\"\" Window based multi-head self attention (W-MSA) module with relative position bias.\n    It supports both of shifted and non-shifted window.\n\n    Args:\n        dim (int): Number of input channels.\n        window_size (tuple[int]): The height and width of the window.\n        num_heads (int): Number of attention heads.\n        qkv_bias (bool, optional):  If True, add a learnable bias to query, key, value. Default: True\n        qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set\n        attn_drop (float, optional): Dropout ratio of attention weight. Default: 0.0\n        proj_drop (float, optional): Dropout ratio of output. Default: 0.0\n    \"\"\"\n\n    def __init__(self, dim, window_size, num_heads, qkv_bias=True, qk_scale=None, attn_drop=0., proj_drop=0.):\n        super().__init__()\n        self.dim = dim\n        self.window_size = window_size  # Wh, Ww\n        self.num_heads = num_heads\n        head_dim = dim // num_heads\n        self.scale = qk_scale or head_dim ** (-0.5)\n\n        # define a parameter table of relative position bias\n        self.relative_position_bias_table = nn.Parameter(\n            torch.zeros((2 * window_size[0] - 1) * (2 * window_size[1] - 1), num_heads))  # 2*Wh-1 * 2*Ww-1, nH\n\n        # get pair-wise relative position index for each token inside the window\n        coords_h = torch.arange(self.window_size[0])\n        coords_w = torch.arange(self.window_size[1])\n        coords = torch.stack(torch.meshgrid([coords_h, coords_w]))  # 2, Wh, Ww\n        coords_flatten = torch.flatten(coords, 1)  # 2, Wh*Ww\n        relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :]  # 2, Wh*Ww, Wh*Ww\n        relative_coords = relative_coords.permute(1, 2, 0).contiguous()  # Wh*Ww, Wh*Ww, 2\n        relative_coords[:, :, 0] += self.window_size[0] - 1  # shift to start from 0\n        relative_coords[:, :, 1] += self.window_size[1] - 1\n        relative_coords[:, :, 0] *= 2 * self.window_size[1] - 1\n        relative_position_index = relative_coords.sum(-1)  # Wh*Ww, Wh*Ww\n        self.register_buffer(\"relative_position_index\", relative_position_index)\n\n        self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)\n        self.proj = nn.Linear(dim, dim)\n        self.softmax = nn.Softmax(dim=-1)\n\n        self.attn_drop = nn.Dropout(attn_drop)\n        self.proj_drop = nn.Dropout(proj_drop)\n\n        trunc_normal_(self.relative_position_bias_table, std=.02)\n\n    def forward(self, x, mask=None):\n        \"\"\"\n        Args:\n            x: input features with shape of (num_windows*B, N, C)\n            mask: (0/-inf) mask with shape of (num_windows, Wh*Ww, Wh*Ww) or None\n        \"\"\"\n\n        B_, N, C = x.shape\n        qkv = self.qkv(x).reshape(B_, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)\n        q, k, v = qkv[0], qkv[1], qkv[2]  # make torchscript happy (cannot use tensor as tuple)\n\n        q = q * self.scale\n        attn = (q @ k.transpose(-2, -1))\n\n        relative_position_bias = \\\n            self.relative_position_bias_table[self.relative_position_index.view(-1)].view(\n                self.window_size[0] * self.window_size[1], self.window_size[0] * self.window_size[1], self.num_heads)\n        # Wh*Ww,Wh*Ww,nH\n        relative_position_bias = relative_position_bias.permute(2, 0, 1).contiguous()  # nH, Wh*Ww, Wh*Ww\n\n        attn = attn + relative_position_bias.unsqueeze(0)\n\n        if mask is not None:\n            nW = mask.shape[0]\n            attn = attn.view(B_ // nW, nW, self.num_heads, N, N) + mask.unsqueeze(1).unsqueeze(0)\n            attn = attn.view(-1, self.num_heads, N, N)\n            attn = self.softmax(attn)\n        else:\n            attn = self.softmax(attn)\n\n        attn = self.attn_drop(attn)\n        x = (attn @ v).transpose(1, 2).reshape(B_, N, C)\n        x = self.proj(x)\n        x = self.proj_drop(x)\n        return x\n\n    def extra_repr(self) -> str:\n        return f'dim={self.dim}, window_size={self.window_size}, num_heads={self.num_heads}'\n\n\n# remove fixed calculate attention mask for SW-MSA\n#\nclass SwinTransformerBlock(nn.Module):\n    r\"\"\" Swin Transformer Block.\n\n    Args:\n        dim (int): Number of input channels.\n        input_resolution (tuple[int]): Input resulotion.\n        num_heads (int): Number of attention heads.\n        window_size (int): Window size.\n        shift_size (int): Shift size for SW-MSA.\n        mlp_ratio (float): Ratio of mlp hidden dim to embedding dim.\n        qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True\n        qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set.\n        drop (float, optional): Dropout rate. Default: 0.0\n        attn_drop (float, optional): Attention dropout rate. Default: 0.0\n        drop_path (float, optional): Stochastic depth rate. Default: 0.0\n        act_layer (nn.Module, optional): Activation layer. Default: nn.GELU\n        norm_layer (nn.Module, optional): Normalization layer.  Default: nn.LayerNorm\n        fused_window_process (bool, optional): If True, use one kernel to fused window shift & window partition for acceleration, similar for the reversed part. Default: False\n    \"\"\"\n\n    def __init__(self,\n                 dim,\n                 num_heads,\n                 window_size=7,\n                 shift_size=0,\n                 mlp_ratio=4.,\n                 qkv_bias=True,\n                 qk_scale=None,\n                 drop=0.,\n                 attn_drop=0.,\n                 drop_path=0.,\n                 act_layer=nn.GELU,\n                 norm_layer=nn.LayerNorm,\n                 ):\n        super().__init__()\n        self.dim = dim\n        self.num_heads = num_heads\n        self.window_size = window_size\n        self.shift_size = shift_size\n        self.mlp_ratio = mlp_ratio\n        assert 0 <= self.shift_size < self.window_size, \"shift_size must in 0-window_size\"\n\n        self.norm1 = norm_layer(dim)\n        self.attn = WindowAttention(\n            dim,\n            window_size=to_2tuple(self.window_size),\n            num_heads=num_heads,\n            qkv_bias=qkv_bias,\n            qk_scale=qk_scale,\n            attn_drop=attn_drop,\n            proj_drop=drop,\n        )\n        self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity()\n\n        self.norm2 = norm_layer(dim)\n        mlp_hidden_dim = int(dim * mlp_ratio)\n        self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop)\n\n    def forward(self, x, H, W, mask_matrix):\n\n        B, L, C = x.shape\n        assert L == H * W, \"input feature has wrong size\"\n\n        shortcut = x\n        x = self.norm1(x)\n        x = x.view(B, H, W, C)\n\n        # pad feature maps to multiples of window size\n        pad_l = pad_t = 0\n        pad_r = (self.window_size - W % self.window_size) % self.window_size\n        pad_b = (self.window_size - H % self.window_size) % self.window_size\n        x = F.pad(x, (0, 0, pad_l, pad_r, pad_t, pad_b))\n        _, Hp, Wp, _ = x.shape\n\n        # cyclic shift ---\n        if self.shift_size > 0:\n            shifted_x = torch.roll(x, shifts=(-self.shift_size, -self.shift_size), dims=(1, 2))\n            attn_mask = mask_matrix\n        else:\n            shifted_x = x\n            attn_mask = None\n\n        x_windows = window_partition(shifted_x, self.window_size)  # nW*B, window_size, window_size, C\n        x_windows = x_windows.view(-1, self.window_size * self.window_size, C)  # nW*B, window_size*window_size, C\n        attn_windows = self.attn(x_windows, mask=attn_mask)  # nW*B, window_size*window_size, C\n        attn_windows = attn_windows.view(-1, self.window_size, self.window_size, C)\n\n        # reverse cyclic shift ---\n        shifted_x = window_reverse(attn_windows, self.window_size, Hp, Wp)  # B H' W' C\n        if self.shift_size > 0:\n            x = torch.roll(shifted_x, shifts=(self.shift_size, self.shift_size), dims=(1, 2))\n        else:\n            x = shifted_x\n\n        if pad_r > 0 or pad_b > 0:\n            x = x[:, :H, :W, :].contiguous()\n        x = x.view(B, H * W, C)\n\n        # FFN\n        x = shortcut + self.drop_path(x)\n        x = x + self.drop_path(self.mlp(self.norm2(x)))\n        return x\n\n    def extra_repr(self) -> str:\n        return f\"dim={self.dim}, num_heads={self.num_heads}, \" \\\n               f\"window_size={self.window_size}, shift_size={self.shift_size}, mlp_ratio={self.mlp_ratio}\"\n\n\n# ---------\n# H,W as input\n# padding\nclass PatchMerging(nn.Module):\n    r\"\"\" Patch Merging Layer.\n\n    Args:\n        dim (int): Number of input channels.\n        norm_layer (nn.Module, optional): Normalization layer.  Default: nn.LayerNorm\n    \"\"\"\n\n    def __init__(self, dim, norm_layer=nn.LayerNorm):\n        super().__init__()\n        self.dim = dim\n        self.reduction = nn.Linear(4 * dim, 2 * dim, bias=False)\n        self.norm = norm_layer(4 * dim)\n\n    def forward(self, x, H, W):\n        \"\"\"\n        Args:\n            x: Input feature, tensor size (B, H*W, C).\n            H, W: Spatial resolution of the input feature.\n        \"\"\"\n\n        B, L, C = x.shape\n        assert L == H * W, \"input feature has wrong size\"\n\n        x = x.view(B, H, W, C)\n        # padding\n        pad_input = (H % 2 == 1) or (W % 2 == 1)\n        if pad_input:\n            x = F.pad(x, (0, 0, 0, W % 2, 0, H % 2))\n\n        x0 = x[:, 0::2, 0::2, :]  # B H/2 W/2 C\n        x1 = x[:, 1::2, 0::2, :]  # B H/2 W/2 C\n        x2 = x[:, 0::2, 1::2, :]  # B H/2 W/2 C\n        x3 = x[:, 1::2, 1::2, :]  # B H/2 W/2 C\n        x = torch.cat([x0, x1, x2, x3], -1)  # B, H/2, W/2, 4*C\n        x = x.view(B, -1, 4 * C)  # B, H/2*W/2, 4*C\n\n        x = self.norm(x)\n        x = self.reduction(x)\n\n        return x\n\n\n# compute  attention mask for SW-MSA\nclass BasicLayer(nn.Module):\n    \"\"\" A basic Swin Transformer layer for one stage.\n\n    Args:\n        dim (int): Number of input channels.\n        depth (int): Number of blocks.\n        num_heads (int): Number of attention heads.\n        window_size (int): Local window size.\n        mlp_ratio (float): Ratio of mlp hidden dim to embedding dim.\n        qkv_bias (bool, optional): If True, add a learnable bias to query, key, value. Default: True\n        qk_scale (float | None, optional): Override default qk scale of head_dim ** -0.5 if set.\n        drop (float, optional): Dropout rate. Default: 0.0\n        attn_drop (float, optional): Attention dropout rate. Default: 0.0\n        drop_path (float | tuple[float], optional): Stochastic depth rate. Default: 0.0\n        norm_layer (nn.Module, optional): Normalization layer. Default: nn.LayerNorm\n        downsample (nn.Module | None, optional): Downsample layer at the end of the layer. Default: None\n        use_checkpoint (bool): Whether to use checkpointing to save memory. Default: False.\n        fused_window_process (bool, optional): If True, use one kernel to fused window shift & window partition for acceleration, similar for the reversed part. Default: False\n    \"\"\"\n\n    def __init__(self,\n                 dim,\n                 depth,\n                 num_heads,\n                 window_size,\n                 mlp_ratio=4.,\n                 qkv_bias=True,\n                 qk_scale=None,\n                 drop=0.,\n                 attn_drop=0.,\n                 drop_path=0.,\n                 norm_layer=nn.LayerNorm,\n                 downsample=None,\n                 # use_checkpoint=False,\n                 ):\n        super().__init__()\n        self.window_size = window_size\n        self.shift_size = window_size // 2\n        self.depth = depth\n\n        self.blocks = nn.ModuleList([\n            SwinTransformerBlock(\n                dim=dim,\n                num_heads=num_heads,\n                window_size=window_size,\n                shift_size=0 if (i % 2 == 0) else window_size // 2,\n                mlp_ratio=mlp_ratio,\n                qkv_bias=qkv_bias,\n                qk_scale=qk_scale,\n                drop=drop,\n                attn_drop=attn_drop,\n                drop_path=drop_path[i] if isinstance(drop_path, list) else drop_path,\n                norm_layer=norm_layer,\n            )\n            for i in range(depth)\n        ])\n        # patch merging layer\n        if downsample is not None:\n            self.downsample = downsample(dim=dim, norm_layer=norm_layer)\n        else:\n            self.downsample = None\n\n    def forward(self, x, H, W):\n        \"\"\"\n        Args:\n            x: Input feature, tensor size (B, H*W, C).\n            H, W: Spatial resolution of the input feature.\n        \"\"\"\n\n        # calculate attention mask for SW-MSA ----\n        Hp = int(np.ceil(H / self.window_size)) * self.window_size\n        Wp = int(np.ceil(W / self.window_size)) * self.window_size\n        img_mask = torch.zeros((1, Hp, Wp, 1), device=x.device)  # 1 Hp Wp 1\n        h_slices = (slice(0, -self.window_size),\n                    slice(-self.window_size, -self.shift_size),\n                    slice(-self.shift_size, None))\n        w_slices = (slice(0, -self.window_size),\n                    slice(-self.window_size, -self.shift_size),\n                    slice(-self.shift_size, None))\n        cnt = 0\n        for h in h_slices:\n            for w in w_slices:\n                img_mask[:, h, w, :] = cnt\n                cnt += 1\n\n        mask_windows = window_partition(img_mask, self.window_size)  # nW, window_size, window_size, 1\n        mask_windows = mask_windows.view(-1, self.window_size * self.window_size)\n        attn_mask = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2)\n        attn_mask = attn_mask.masked_fill(attn_mask != 0, float(-100.0)).masked_fill(attn_mask == 0, float(0.0))\n        # ------\n\n        for blk in self.blocks:\n            x = blk(x, H, W, attn_mask)\n\n        if self.downsample is not None:\n            x_down = self.downsample(x, H, W)\n            Wh, Ww = (H + 1) // 2, (W + 1) // 2\n            return x, H, W, x_down, Wh, Ww\n        else:\n            return x, H, W, x, H, W\n\n\n# ---------------------------------------------\n# https://github.com/facebookresearch/ConvNeXt/blob/main/models/convnext.py#L15\nclass LayerNorm2d(nn.Module):\n    def __init__(self, dim, eps=1e-6):\n        super().__init__()\n        self.weight = nn.Parameter(torch.ones(dim))\n        self.bias = nn.Parameter(torch.zeros(dim))\n        self.eps = eps\n\n    def forward(self, x):\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","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import math\nimport copy\nfrom functools import partial\nfrom collections import OrderedDict\nfrom typing import Optional, Callable\nfrom torch import Tensor\n\ndef _make_divisible(ch, divisor=8, min_ch=None):\n    \"\"\"\n    This function is taken from the original tf repo.\n    It ensures that all layers have a channel number that is divisible by 8\n    It can be seen here:\n    https://github.com/tensorflow/models/blob/master/research/slim/nets/mobilenet/mobilenet.py\n    \"\"\"\n    if min_ch is None:\n        min_ch = divisor\n    new_ch = max(min_ch, int(ch + divisor / 2) // divisor * divisor)\n    # Make sure that round down does not go down by more than 10%.\n    if new_ch < 0.9 * ch:\n        new_ch += divisor\n    return new_ch\n\n\ndef drop_path(x, drop_prob: float = 0., training: bool = False):\n    \"\"\"\n    Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).\n    \"Deep Networks with Stochastic Depth\", https://arxiv.org/pdf/1603.09382.pdf\n    This function is taken from the rwightman.\n    It can be seen here:\n    https://github.com/rwightman/pytorch-image-models/blob/master/timm/models/layers/drop.py#L140\n    \"\"\"\n    if drop_prob == 0. or not training:\n        return x\n    keep_prob = 1 - drop_prob\n    shape = (x.shape[0],) + (1,) * (x.ndim - 1)  # work with diff dim tensors, not just 2D ConvNets\n    random_tensor = keep_prob + torch.rand(shape, dtype=x.dtype, device=x.device)\n    random_tensor.floor_()  # binarize\n    output = x.div(keep_prob) * random_tensor\n    return output\n\n\nclass ConvBNActivation(nn.Sequential):\n    def __init__(self,\n                 in_planes: int,\n                 out_planes: int,\n                 kernel_size: int = 3,\n                 stride: int = 1,\n                 groups: int = 1,\n                 norm_layer: Optional[Callable[..., nn.Module]] = None,\n                 activation_layer: Optional[Callable[..., nn.Module]] = None):\n        padding = (kernel_size - 1) // 2\n        if norm_layer is None:\n            norm_layer = nn.BatchNorm2d\n        if activation_layer is None:\n            activation_layer = nn.SiLU  # alias Swish  (torch>=1.7)\n\n        super(ConvBNActivation, self).__init__(nn.Conv2d(in_channels=in_planes,\n                                                         out_channels=out_planes,\n                                                         kernel_size=kernel_size,\n                                                         stride=stride,\n                                                         padding=padding,\n                                                         groups=groups,\n                                                         bias=False),\n                                               norm_layer(out_planes),\n                                               activation_layer())\n\n\nclass SqueezeExcitation(nn.Module):\n    def __init__(self,\n                 input_c: int,   # block input channel\n                 expand_c: int,  # block expand channel\n                 squeeze_factor: int = 4):\n        super(SqueezeExcitation, self).__init__()\n        squeeze_c = input_c // squeeze_factor\n        self.fc1 = nn.Conv2d(expand_c, squeeze_c, 1)\n        self.ac1 = nn.SiLU()  # alias Swish\n        self.fc2 = nn.Conv2d(squeeze_c, expand_c, 1)\n        self.ac2 = nn.Sigmoid()\n\n    def forward(self, x: Tensor) -> Tensor:\n        scale = F.adaptive_avg_pool2d(x, output_size=(1, 1))\n        scale = self.fc1(scale)\n        scale = self.ac1(scale)\n        scale = self.fc2(scale)\n        scale = self.ac2(scale)\n        return scale * x\n\n\nclass InvertedResidualConfig:\n    # kernel_size, in_channel, out_channel, exp_ratio, strides, use_SE, drop_connect_rate\n    def __init__(self,\n                 kernel: int,          # 3 or 5\n                 input_c: int,\n                 out_c: int,\n                 expanded_ratio: int,  # 1 or 6\n                 stride: int,          # 1 or 2\n                 use_se: bool,         # True\n                 drop_rate: float,\n                 index: str,           # 1a, 2a, 2b, ...\n                 width_coefficient: float):\n        self.input_c = self.adjust_channels(input_c, width_coefficient)\n        self.kernel = kernel\n        self.expanded_c = self.input_c * expanded_ratio\n        self.out_c = self.adjust_channels(out_c, width_coefficient)\n        self.use_se = use_se\n        self.stride = stride\n        self.drop_rate = drop_rate\n        self.index = index\n\n    @staticmethod\n    def adjust_channels(channels: int, width_coefficient: float):\n        return _make_divisible(channels * width_coefficient, 8)\n\n\nclass InvertedResidual(nn.Module):\n    def __init__(self,\n                 cnf: InvertedResidualConfig,\n                 norm_layer: Callable[..., nn.Module]):\n        super(InvertedResidual, self).__init__()\n\n        if cnf.stride not in [1, 2]:\n            raise ValueError(\"illegal stride value.\")\n\n        self.use_res_connect = (cnf.stride == 1 and cnf.input_c == cnf.out_c)\n\n        layers = OrderedDict()\n        activation_layer = nn.SiLU  # alias Swish\n\n        # expand\n        if cnf.expanded_c != cnf.input_c:\n            layers.update({\"expand_conv\": ConvBNActivation(cnf.input_c,\n                                                           cnf.expanded_c,\n                                                           kernel_size=1,\n                                                           norm_layer=norm_layer,\n                                                           activation_layer=activation_layer)})\n\n        # depthwise\n        layers.update({\"dwconv\": ConvBNActivation(cnf.expanded_c,\n                                                  cnf.expanded_c,\n                                                  kernel_size=cnf.kernel,\n                                                  stride=cnf.stride,\n                                                  groups=cnf.expanded_c,\n                                                  norm_layer=norm_layer,\n                                                  activation_layer=activation_layer)})\n\n        if cnf.use_se:\n            layers.update({\"se\": SqueezeExcitation(cnf.input_c,\n                                                   cnf.expanded_c)})\n\n        # project\n        layers.update({\"project_conv\": ConvBNActivation(cnf.expanded_c,\n                                                        cnf.out_c,\n                                                        kernel_size=1,\n                                                        norm_layer=norm_layer,\n                                                        activation_layer=nn.Identity)})\n\n        self.block = nn.Sequential(layers)\n        self.out_channels = cnf.out_c\n        self.is_strided = cnf.stride > 1\n\n        # 只有在使用shortcut连接时才使用dropout层\n        if self.use_res_connect and cnf.drop_rate > 0:\n            self.dropout = DropPath(cnf.drop_rate)\n        else:\n            self.dropout = nn.Identity()\n\n    def forward(self, x: Tensor) -> Tensor:\n        result = self.block(x)\n        result = self.dropout(result)\n        if self.use_res_connect:\n            result += x\n\n        return result\n\nclass EfficientNet(nn.Module):\n    def __init__(self,\n                 width_coefficient: float,\n                 depth_coefficient: float,\n                 num_classes: int = 1000,\n                 dropout_rate: float = 0.2,\n                 drop_connect_rate: float = 0.2,\n                 block: Optional[Callable[..., nn.Module]] = None,\n                 norm_layer: Optional[Callable[..., nn.Module]] = None\n                 ):\n        super(EfficientNet, self).__init__()\n\n        # kernel_size, in_channel, out_channel, exp_ratio, strides, use_SE, drop_connect_rate, repeats\n        default_cnf = [[3, 32, 64, 1, 1, True, drop_connect_rate, 1],\n                       [3, 64, 96, 6, 1, True, drop_connect_rate, 2],]\n\n        def round_repeats(repeats):\n            \"\"\"Round number of repeats based on depth multiplier.\"\"\"\n            return int(math.ceil(depth_coefficient * repeats))\n\n        if block is None:\n            block = InvertedResidual\n\n        if norm_layer is None:\n            norm_layer = partial(nn.BatchNorm2d, eps=1e-3, momentum=0.1)\n\n        adjust_channels = partial(InvertedResidualConfig.adjust_channels,\n                                  width_coefficient=width_coefficient)\n\n        # build inverted_residual_setting\n        bneck_conf = partial(InvertedResidualConfig,\n                             width_coefficient=width_coefficient)\n\n        b = 0\n        num_blocks = float(sum(round_repeats(i[-1]) for i in default_cnf))\n        inverted_residual_setting = []\n        for stage, args in enumerate(default_cnf):\n            cnf = copy.copy(args)\n            for i in range(round_repeats(cnf.pop(-1))):\n                if i > 0:\n                    # strides equal 1 except first cnf\n                    cnf[-3] = 1  # strides\n                    cnf[1] = cnf[2]  # input_channel equal output_channel\n\n                cnf[-1] = args[-2] * b / num_blocks  # update dropout ratio\n                index = chr(i + 97) + str(stage + 1)   # 1a, 2a, 2b, ...\n                inverted_residual_setting.append(bneck_conf(*cnf, index))\n                b += 1\n\n        # create layers\n        layers = OrderedDict()\n\n        # first conv\n        layers.update({\"stem_conv\": ConvBNActivation(in_planes=3,\n                                                     out_planes=adjust_channels(32),\n                                                     kernel_size=3,\n                                                     stride=2,\n                                                     norm_layer=norm_layer)})\n\n        # building inverted residual blocks\n        for cnf in inverted_residual_setting:\n            layers.update({cnf.index: block(cnf, norm_layer)})\n\n        # build top\n        last_conv_input_c = inverted_residual_setting[-1].out_c\n        last_conv_output_c = adjust_channels(1280)\n        layers.update({\"top\": ConvBNActivation(in_planes=last_conv_input_c,\n                                               out_planes=last_conv_output_c,\n                                               kernel_size=1,\n                                               norm_layer=norm_layer)})\n        self.features = nn.Sequential(layers)\n        self.avgpool = nn.AdaptiveAvgPool2d(1)\n        self.enc0 = self.features.stem_conv\n        self.enc1 = self.features.a1\n        self.enc2 = self.features.a2\n\n        # initial weights\n        for m in self.modules():\n            if isinstance(m, nn.Conv2d):\n                nn.init.kaiming_normal_(m.weight, mode=\"fan_out\")\n                if m.bias is not None:\n                    nn.init.zeros_(m.bias)\n            elif isinstance(m, nn.BatchNorm2d):\n                nn.init.ones_(m.weight)\n                nn.init.zeros_(m.bias)\n            elif isinstance(m, nn.Linear):\n                nn.init.normal_(m.weight, 0, 0.01)\n                nn.init.zeros_(m.bias)\n\n    def _forward_impl(self, x: Tensor) -> Tensor:\n        x = self.enc0(x)\n        x = self.enc1(x)\n        x = self.enc2(x)\n        return x\n\n    def forward(self, x: Tensor) -> Tensor:\n        return self._forward_impl(x)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class SwinTransformerV1(nn.Module):\n    def __init__(self,\n                 pretrain_img_size=224,\n                 patch_size=4,\n                 in_chans=3,\n                 embed_dim=96,\n                 depths=[2, 2, 6, 2],\n                 num_heads=[3, 6, 12, 24],\n                 window_size=7,\n                 mlp_ratio=4.,\n                 qkv_bias=True,\n                 qk_scale=None,\n                 drop_rate=0.,\n                 attn_drop_rate=0.,\n                 drop_path_rate=0.1,\n                 norm_layer=nn.LayerNorm,\n                 patch_norm=True,\n                 out_norm=nn.Identity,  # use nn.Identity, nn.BatchNorm2d, LayerNorm2d\n                 **kwargs\n                 ):\n        super().__init__()\n        self.pretrain_img_size = pretrain_img_size\n        self.num_layers = len(depths)\n        self.embed_dim = embed_dim\n        self.mlp_ratio = mlp_ratio\n\n        self.patch_embed = EfficientNet(width_coefficient=1.0,\n                           depth_coefficient=1.0,\n                           dropout_rate=0.2)\n        self.pos_drop = nn.Dropout(p=drop_rate)\n\n        # stochastic depth\n        dpr = np.linspace(0, drop_path_rate, sum(depths)).tolist()  # stochastic depth decay rule\n\n        # build layers\n        self.layers = nn.ModuleList()\n        for i in range(self.num_layers):\n            layer = BasicLayer(\n                dim=int(embed_dim * 2 ** i),\n                depth=depths[i],\n                num_heads=num_heads[i],\n                window_size=window_size,\n                mlp_ratio=self.mlp_ratio,\n                qkv_bias=qkv_bias, qk_scale=qk_scale,\n                drop=drop_rate,\n                attn_drop=attn_drop_rate,\n                drop_path=dpr[sum(depths[:i]):sum(depths[:i + 1])],\n                norm_layer=norm_layer,\n                downsample=PatchMerging if (i < self.num_layers - 1) else None,\n            )\n            self.layers.append(layer)\n\n        # ---\n        # add a norm layer for each output\n        self.out_norm = nn.ModuleList(\n            [out_norm(int(embed_dim * 2 ** i)) for i in range(self.num_layers)]\n        )\n\n        # ---\n        self.apply(self._init_weights)\n\n    def _init_weights(self, m):\n        if isinstance(m, nn.Linear):\n            trunc_normal_(m.weight, std=.02)\n            if isinstance(m, nn.Linear) and m.bias is not None:\n                nn.init.constant_(m.bias, 0)\n        elif isinstance(m, nn.LayerNorm):\n            nn.init.constant_(m.bias, 0)\n            nn.init.constant_(m.weight, 1.0)\n\n    def forward(self, x):\n        x = self.patch_embed(x)\n        Wh, Ww = x.size(2), x.size(3)\n\n        # positional encode?\n        x = x.flatten(2).transpose(1, 2)\n        x = self.pos_drop(x)\n\n        outs = []\n        for i in range(self.num_layers):\n            x_out, H, W, x, Wh, Ww = self.layers[i](x, Wh, Ww)\n            out = x_out.view(-1, H, W, int(self.embed_dim * 2 ** i)).permute(0, 3, 1, 2).contiguous()\n            out = self.out_norm[i](out)\n            outs.append(out)\n\n        return outs\n\n\n#####################################################################################################################\n\n\npretrain_dir = '/kaggle/input/swin-tiny-small-22k-pretrained/'\n# https://github.com/microsoft/Swin-Transformer\ncfg = dict(\n    #configs/_base_/models/upernet_swin.py\n    basic = dict(\n        swin=dict(\n            embed_dim=96,\n            depths=[2, 2, 6, 2],\n            num_heads=[3, 6, 12, 24],\n            window_size=7,\n            mlp_ratio=4.,\n            qkv_bias=True,\n            qk_scale=None,\n            drop_rate=0.,\n            attn_drop_rate=0.,\n            drop_path_rate=0.3,\n            ape=False,\n            patch_norm=True,\n            out_indices=(0, 1, 2, 3),\n            use_checkpoint=False\n        ),\n\n    ),\n\n    #configs/swin/upernet_swin_tiny_patch4_window7_512x512_160k_ade20k.py\n    swin_tiny_patch4_window7_224=dict(\n        checkpoint = pretrain_dir+'/swin_tiny_patch4_window7_224_22k.pth',\n\n        swin = dict(\n            embed_dim=96,\n            depths=[2, 2, 6, 2],\n            num_heads=[3, 6, 12, 24],\n            window_size=7,\n            ape=False,\n            drop_path_rate=0.3,\n            patch_norm=True,\n            use_checkpoint=False,\n        ),\n        upernet=dict(\n            in_channels=[96, 192, 384, 768],\n        ),\n    ),\n\n    #/configs/swin/upernet_swin_small_patch4_window7_512x512_160k_ade20k.py\n    swin_small_patch4_window7_224_22k=dict(\n        checkpoint = pretrain_dir+'/swin_small_patch4_window7_224_22k.pth',\n\n        swin = dict(\n            embed_dim=96,\n            depths=[2, 2, 18, 2],\n            num_heads=[3, 6, 12, 24],\n            window_size=7,\n            ape=False,\n            drop_path_rate=0.3,\n            patch_norm=True,\n            use_checkpoint=False\n        ),\n        upernet=dict(\n            in_channels=[96, 192, 384, 768],\n        ),\n    ),\n)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def conv3x3_bn_relu(in_planes, out_planes, stride=1):\n    \"3x3 convolution + BN + relu\"\n    return nn.Sequential(\n        nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False),\n        nn.BatchNorm2d(out_planes),\n        nn.ReLU(inplace=True),\n    )\n\n# upernet\nclass UPerDecoder(nn.Module):\n    def __init__(self,\n                 in_dim=[256, 512, 1024, 2048],\n                 ppm_pool_scale=[1, 2, 3, 6],\n                 ppm_dim=512,\n                 fpn_out_dim=256\n                 ):\n        super(UPerDecoder, self).__init__()\n\n        # PPM ----\n        dim = in_dim[-1]\n        ppm_pooling = []\n        ppm_conv = []\n\n        for scale in ppm_pool_scale:\n            ppm_pooling.append(\n                nn.AdaptiveAvgPool2d(scale)\n            )\n            ppm_conv.append(\n                nn.Sequential(\n                    nn.Conv2d(dim, ppm_dim, kernel_size=1, bias=False),\n                    nn.BatchNorm2d(ppm_dim),\n                    nn.ReLU(inplace=True)\n                )\n            )\n        self.ppm_pooling = nn.ModuleList(ppm_pooling)\n        self.ppm_conv = nn.ModuleList(ppm_conv)\n        self.ppm_out = conv3x3_bn_relu(dim + len(ppm_pool_scale) * ppm_dim, fpn_out_dim, 1)\n\n        # FPN ----\n        fpn_in = []\n        for i in range(0, len(in_dim) - 1):  # skip the top layer\n            fpn_in.append(\n                nn.Sequential(\n                    nn.Conv2d(in_dim[i], fpn_out_dim, kernel_size=1, bias=False),\n                    nn.BatchNorm2d(fpn_out_dim),\n                    nn.ReLU(inplace=True)\n                )\n            )\n        self.fpn_in = nn.ModuleList(fpn_in)\n\n        fpn_out = []\n        for i in range(len(in_dim) - 1):  # skip the top layer\n            fpn_out.append(\n                conv3x3_bn_relu(fpn_out_dim, fpn_out_dim, 1),\n            )\n        self.fpn_out = nn.ModuleList(fpn_out)\n\n        self.fpn_fuse = nn.Sequential(\n            conv3x3_bn_relu(len(in_dim) * fpn_out_dim, fpn_out_dim, 1),\n        )\n\n    def forward(self, feature):\n        f = feature[-1]\n        pool_shape = f.shape[2:]\n\n        ppm_out = [f]\n        for pool, conv in zip(self.ppm_pooling, self.ppm_conv):\n            p = pool(f)\n            p = F.interpolate(p, size=pool_shape, mode='bilinear', align_corners=False)\n            p = conv(p)\n            ppm_out.append(p)\n        ppm_out = torch.cat(ppm_out, 1)\n        down = self.ppm_out(ppm_out)\n\n        # --------------------------------------\n        fpn_out = [down]\n        for i in reversed(range(len(feature) - 1)):\n            lateral = feature[i]\n            lateral = self.fpn_in[i](lateral)  # lateral branch\n            down = F.interpolate(down, size=lateral.shape[2:], mode='bilinear', align_corners=False)  # top-down branch\n            down = down + lateral\n            fpn_out.append(self.fpn_out[i](down))\n\n        fpn_out.reverse()  # [P2 - P5]\n        fusion_shape = fpn_out[0].shape[2:]\n        fusion = [fpn_out[0]]\n        for i in range(1, len(fpn_out)):\n            fusion.append(\n                F.interpolate(fpn_out[i], fusion_shape, mode='bilinear', align_corners=False)\n            )\n        x = self.fpn_fuse(torch.cat(fusion, 1))\n\n        return x, fusion","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class Net(nn.Module):\n    def __init__( self,num_class=8):\n        super(Net, self).__init__()\n\n        self.arch = 'swin_tiny_patch4_window7_224'\n\n        self.encoder = SwinTransformerV1(\n            ** {**cfg['basic']['swin'], **cfg[self.arch]['swin'],\n                **{'out_norm' : LayerNorm2d} }\n        )\n        encoder_dim =cfg[self.arch]['upernet']['in_channels']\n        #[96, 192, 384, 768]\n\n        self.decoder = UPerDecoder(\n            in_dim=encoder_dim,\n            ppm_pool_scale=[1, 2, 3, 6],\n            ppm_dim=512,\n            fpn_out_dim=256\n        )\n\n        self.logit = nn.Sequential(\n            nn.Conv2d(256, num_class, kernel_size=1)\n        )\n\n\n    def forward(self, x):\n        B,C,H,W = x.shape\n        encoder = self.encoder(x)\n        last, decoder = self.decoder(encoder)\n        logit = self.logit(last)\n        logit = F.interpolate(logit, size=None, scale_factor=2, mode='bilinear', align_corners=False)\n        return logit","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"maskloss1 = smp.losses.SoftCrossEntropyLoss(smooth_factor=0.05, ignore_index=8)\nmaskloss2 = smp.losses.DiceLoss(mode='multiclass', smooth=0.05, ignore_index=8)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 训练验证","metadata":{}},{"cell_type":"code","source":"def train_one_epoch(model, optimizer, scheduler, dataloader, device, epoch):\n    model.train()\n    scaler = amp.GradScaler()\n    \n    dataset_size = 0\n    running_loss = 0.0\n    confuse_mat = np.zeros([8, 8])\n    pbar = tqdm(enumerate(dataloader), total=len(dataloader), desc='Train ')\n    for step, (images, masks) in pbar:         \n        images = images.to(device, dtype=torch.float)\n        masks  = masks.to(device)\n        \n        batch_size = images.size(0)\n        \n        with amp.autocast(enabled=True):\n            y_pred = model(images)\n            loss = 0.5 * maskloss1(y_pred, masks.long()) + 0.5 * maskloss2(y_pred, masks.long())\n            loss   = loss / CFG.n_accumulate\n            \n        scaler.scale(loss).backward()\n        if (step + 1) % CFG.n_accumulate == 0:\n            scaler.step(optimizer)\n            scaler.update()\n            # zero the parameter gradients\n            optimizer.zero_grad()\n            if scheduler is not None:\n                scheduler.step()\n                \n        running_loss += (loss.item() * batch_size)\n        dataset_size += batch_size\n        \n        epoch_loss = running_loss / dataset_size\n        \n        mem = torch.cuda.memory_reserved() / 1E9 if torch.cuda.is_available() else 0\n        current_lr = optimizer.param_groups[0]['lr']\n        pbar.set_postfix(train_loss=f'{epoch_loss:0.4f}',\n                        lr=f'{current_lr:0.5f}',\n                        gpu_mem=f'{mem:0.2f} GB')\n    torch.cuda.empty_cache()\n    gc.collect()\n    \n    return epoch_loss","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"@torch.no_grad()\ndef valid_one_epoch(model, dataloader, device, epoch):\n    model.eval()\n    \n    dataset_size = 0\n    running_loss = 0.0\n    \n    val_scores = []\n    \n    pbar = tqdm(enumerate(dataloader), total=len(dataloader), desc='Valid ')\n    for step, (images, masks) in pbar:        \n        images  = images.to(device, dtype=torch.float)\n        masks   = masks.to(device)\n        batch_size = images.size(0)\n        y_pred  = model(images)\n        loss    =0.5 * maskloss1(y_pred, masks.long()) + 0.5 * maskloss2(y_pred, masks.long())\n        \n        running_loss += (loss.item() * batch_size)\n        dataset_size += batch_size\n        epoch_loss = running_loss / dataset_size\n        y_pred = nn.Sigmoid()(y_pred)\n        \n        mem = torch.cuda.memory_reserved() / 1E9 if torch.cuda.is_available() else 0\n        current_lr = optimizer.param_groups[0]['lr']\n        pbar.set_postfix(valid_loss=f'{epoch_loss:0.4f}',\n                        lr=f'{current_lr:0.5f}',\n                        gpu_memory=f'{mem:0.2f} GB')\n    torch.cuda.empty_cache()\n    gc.collect()\n    return epoch_loss","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def run_training(model, optimizer, scheduler, device, num_epochs): \n    start = time.time()\n    best_model_wts = copy.deepcopy(model.state_dict())\n    best_loss      = np.inf\n    best_epoch     = -1\n    history = defaultdict(list)\n    \n    for epoch in range(1, num_epochs + 1): \n        gc.collect()\n        print(f'Epoch {epoch}/{num_epochs}', end='')\n        train_loss = train_one_epoch(model, optimizer, scheduler, \n                                           dataloader=train_loader, \n                                           device=CFG.device, epoch=epoch)\n        \n        val_loss = valid_one_epoch(model, valid_loader, \n                                                 device=CFG.device, \n                                                 epoch=epoch)\n        # deep copy the model\n        if val_loss <= best_loss:\n            print(f\"Valid loss Improved ({best_loss:0.4f} ---> {val_loss:0.4f})\")\n            best_loss    = val_loss\n            best_epoch   = epoch\n            best_model_wts = copy.deepcopy(model.state_dict())\n            PATH = f\"best_epoch-{fold:02d}.pth\"\n            torch.save(model.state_dict(), PATH)\n            print(f\"Model Saved\")\n            \n        last_model_wts = copy.deepcopy(model.state_dict())\n        PATH = f\"last_epoch-{fold:02d}.pth\"\n        torch.save(model.state_dict(), PATH)\n    \n    return model, history","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 学习率策略","metadata":{"execution":{"iopub.status.busy":"2022-08-16T02:24:33.582589Z","iopub.execute_input":"2022-08-16T02:24:33.582959Z","iopub.status.idle":"2022-08-16T02:24:33.587542Z","shell.execute_reply.started":"2022-08-16T02:24:33.582927Z","shell.execute_reply":"2022-08-16T02:24:33.586192Z"}}},{"cell_type":"code","source":"def fetch_scheduler(optimizer):\n    if CFG.scheduler == 'CosineAnnealingLR':\n        scheduler = lr_scheduler.CosineAnnealingLR(optimizer,T_max=CFG.T_max, \n                                                   eta_min=CFG.min_lr)\n    elif CFG.scheduler == 'CosineAnnealingWarmRestarts':\n        scheduler = lr_scheduler.CosineAnnealingWarmRestarts(optimizer,T_0=CFG.T_0, \n                                                             eta_min=CFG.min_lr)\n    elif CFG.scheduler == 'ReduceLROnPlateau':\n        scheduler = lr_scheduler.ReduceLROnPlateau(optimizer,\n                                                   mode='min',\n                                                   factor=0.1,\n                                                   patience=7,\n                                                   threshold=0.0001,\n                                                   min_lr=CFG.min_lr,)\n    elif CFG.scheduer == 'ExponentialLR':\n        scheduler = lr_scheduler.ExponentialLR(optimizer, gamma=0.85)\n    elif CFG.scheduler == None:\n        return None\n    return scheduler","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for fold in range(1):\n    print(f'#'*15)\n    print(f'### Fold: {fold}')\n    print(f'#'*15)\n    train_loader, valid_loader = prepare_loaders(fold=fold, debug=CFG.debug)\n    model     = Net().cuda()\n    optimizer = optim.Adam(model.parameters(), lr=CFG.lr, weight_decay=CFG.wd)\n    scheduler = fetch_scheduler(optimizer)\n    model, history = run_training(model, optimizer, scheduler,\n                                  device=CFG.device,\n                                  num_epochs=CFG.epochs)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 测试数据准备","metadata":{}},{"cell_type":"code","source":"test_path = glob.glob('../input/boolart-cityscapes/data/test/*/*')\ntest_df = pd.DataFrame({\n    \"image_path\":test_path\n})","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class BuildDataset(torch.utils.data.Dataset):\n    def __init__(self, df, label=True, transforms=None):\n        self.df         = df\n        self.label      = label\n        self.img_paths  = df['image_path'].tolist()\n        self.transforms = transforms\n        \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, index):\n        img_path  = self.img_paths[index]\n        img = cv2.imread(img_path)\n        h, w = img.shape[:2]\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n        if self.transforms:\n            data = self.transforms(image=img)\n            img  = data['image']\n        img = np.transpose(img, (2, 0, 1))\n        return torch.tensor(img), os.path.basename(img_path),h,w","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def rle_to_string(runs):\n    return ' '.join(str(x) for x in runs)\n# rle编码\ndef rle_encode(mask):\n    pixels = mask.T.flatten()\n    use_padding = False\n    if pixels[0] or pixels[-1]:\n        use_padding = True\n        pixel_padded = np.zeros([len(pixels) + 2], dtype=pixels.dtype)\n        pixel_padded[1:-1] = pixels\n        pixels = pixel_padded\n    rle = np.where(pixels[1:] != pixels[:-1])[0] + 2\n    if use_padding:\n        rle = rle - 1\n    rle[1::2] = rle[1::2] - rle[:-1:2]\n    return rle_to_string(rle)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 推理","metadata":{}},{"cell_type":"code","source":"@torch.no_grad()\ndef inference(model, dataloader, device):\n    model.eval()\n    pred_strings = []\n    pred_ids = []\n    pred_classes = []\n    pbar = tqdm(enumerate(dataloader), total=len(dataloader), desc='Valid ')\n    for step, (images, id_, height,width) in pbar:        \n        images  = images.to(device, dtype=torch.float)\n        batch_size = images.size(0)\n        y_pred  = model(images).squeeze()\n        y_pred = torch.nn.Sigmoid()(y_pred)\n        y_pred = (y_pred.permute((1, 2, 0))>0.45).to(torch.uint8).cpu().detach().numpy()\n        y_pred = cv2.resize(y_pred, \n                        dsize=(int(width),int(height)),\n                        interpolation=cv2.INTER_NEAREST) \n        rle = [None]*8\n        for midx in range(8):\n            rle[midx] = rle_encode(y_pred[...,midx])\n            file_name = id_[0][:-4]+ str(midx)+'.png'\n            pred_ids.extend([file_name])\n        pred_strings.extend(rle)\n        pred_classes.extend(['other', 'road', 'person', 'rider','car', 'truck','bus','bicycle'])\n    return pred_strings, pred_ids, pred_classes","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_dataset = BuildDataset(test_df, transforms=data_transforms['valid'])\ntest_loader = DataLoader(test_dataset, batch_size=1, \n                          num_workers=1, shuffle=False, pin_memory=True, drop_last=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.load_state_dict(torch.load('./last_epoch-00.pth'))\npred_strings, pred_ids, pred_classes = inference(model, test_loader, device=CFG.device,)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# pred_ids = sum(pred_ids,[]) # 将列表展开\ndf = pd.DataFrame({\n    \"id\":pred_ids,\n    \"predict\":pred_strings\n})\ndf.to_csv('./submission.csv',index=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df.head(10)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}