{"metadata":{"accelerator":"GPU","colab":{"gpuType":"V100","machine_shape":"hm","provenance":[]},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":53666,"databundleVersionId":6589269,"sourceType":"competition"},{"sourceId":7124834,"sourceType":"datasetVersion","datasetId":4109978},{"sourceId":144358840,"sourceType":"kernelVersion"}],"dockerImageVersionId":30559,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true},"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"### setting\n- dataset: seg\n- model: Spec2DCNN\n- feature_extractor: CNNSpectrogram\n- decoder: UNet1DDecoder\n\n### ref\nhttps://github.com/tubo213/kaggle-child-mind-institute-detect-sleep-states","metadata":{"id":"yNV-QMDDj7g3"}},{"cell_type":"code","source":"# settings #########\nex = 260 # exp No.\nfe = 260\n##################\n\nimport os\n\ndef is_colab_environment():\n    return \"COLAB_GPU\" in os.environ\n\ndef is_kaggle_kernel():\n    return os.path.exists('/kaggle/working')\n\nif is_colab_environment():\n\n    from google.colab import drive\n    drive.mount('/content/drive/')\n    !pip install pytorch-lightning\n    !pip install segmentation-models-pytorch\n    !pip install polars==0.19.2\n    BASE_DIR = \"/content/drive/MyDrive/kaggle-child-mind-institute-detect-sleep-states\"\n    data_dir = BASE_DIR + \"/data\"\n    processed_dir = BASE_DIR + f\"/exp_data/fe{fe}\"\n    model_dir = BASE_DIR + f\"/exp_data/exp{ex}\"\n    sub_dir = BASE_DIR + f\"/exp_data/exp{ex}\"\n\nelif is_kaggle_kernel():\n    !pip install segmentation_models_pytorch==0.3.3 --no-index --find-links=/kaggle/input/ex-library\n    BASE_DIR = \"/kaggle\"\n    data_dir = BASE_DIR + \"/input/child-mind-institute-detect-sleep-states\"\n    model_dir = BASE_DIR + f\"/input/cmi-model-ex{ex}/exp{ex}\"\n    processed_dir = BASE_DIR + f\"/working\"\n    sub_dir = BASE_DIR + f\"/working\"","metadata":{"executionInfo":{"elapsed":17512,"status":"ok","timestamp":1701697280940,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"},"user_tz":-540},"id":"P37FfnmXnyuo","outputId":"15e9728f-5a32-4a50-9709-e5642eb99198","execution":{"iopub.status.busy":"2023-12-05T00:11:12.464644Z","iopub.execute_input":"2023-12-05T00:11:12.464942Z","iopub.status.idle":"2023-12-05T00:11:30.640465Z","shell.execute_reply.started":"2023-12-05T00:11:12.464915Z","shell.execute_reply":"2023-12-05T00:11:30.639536Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport polars as pl\nimport random\n\nfrom pathlib import Path\n\nimport torch\nfrom torch.utils.data import Dataset\nfrom pytorch_lightning import Trainer, seed_everything\nfrom pytorch_lightning.callbacks import (\n    LearningRateMonitor,\n    ModelCheckpoint,\n    RichModelSummary,\n    RichProgressBar,\n)\n\nfrom torchvision.transforms.functional import resize","metadata":{"id":"lLijdbPA9mz3","executionInfo":{"status":"ok","timestamp":1701697280940,"user_tz":-540,"elapsed":10,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"execution":{"iopub.status.busy":"2023-12-05T00:11:30.642258Z","iopub.execute_input":"2023-12-05T00:11:30.642561Z","iopub.status.idle":"2023-12-05T00:11:45.264915Z","shell.execute_reply.started":"2023-12-05T00:11:30.642534Z","shell.execute_reply":"2023-12-05T00:11:45.264119Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### setting","metadata":{"id":"dYEgVgoej7hB"}},{"cell_type":"code","source":"class CFG:\n    seed = 0\n    exp_name = 'dummy'\n    # phase = 'train'\n    phase = 'test'\n\n    data_dir = data_dir\n    processed_dir = processed_dir\n    # output_dir = BASE_DIR + f\"/exp_data/exp{ex}\"\n    model_dir = model_dir\n    sub_dir = sub_dir\n\n    features = [\n                \"anglez\",\n                \"enmo\",\n                \"step\",\n                \"hour_sin\",\n                \"hour_cos\",\n                    ]\n\n    duration = 5760 # duration * 5 = 秒数 1日は 17,280 * 5 秒 durationは32の倍数\n    downsample_rate = 2 # durationをdownsample_rateで割った値がnum_framesになる\n    upsample_rate = 1\n    batch_size = 32\n    num_workers = 2\n    # bg_sampling_rate = 0.5\n\n    # aug\n    mixup_prob = 0\n    mixup_alpha = 0 \n    cutmix_prob = 0\n    cutmix_alpha = 0\n\n    labels = [\"awake\", \"event_onset\", \"event_wakeup\"]\n\n    # trainer\n    epochs = 50\n    accelerator = \"auto\"\n    use_amp = True\n    debug = False\n    gradient_clip_val = 1.0\n    accumulate_grad_batches = 1\n    monitor = \"val_loss\"\n    monitor_mode = \"min\"\n    check_val_every_n_epoch = 1\n\n    # optimizer\n    lr = 0.0005\n\n    # scheduler\n    num_warmup_steps = 0\n\n    # CNNSpectrogram\n    base_filters = 64\n    kernel_sizes = [32, 16, downsample_rate]\n    stride: downsample_rate\n    sigmoid = True\n    reinit = True\n\n    # UNet1DDecoder\n    bilinear = False\n    se = False\n    res = False\n    scale_factor = 2\n    dropout = 0.2\n\n    # Spec2DCNN\n    encoder_name = \"resnet34\"\n    encoder_weights = \"imagenet\"\n\n    # post_process\n    score_th = 0.005\n    distance = 40\n\n    # dataset\n    offset = 10\n    sigma = 10\n\n\ncfg = CFG()","metadata":{"id":"Hbi5xhwzj7hB","executionInfo":{"status":"ok","timestamp":1701697280940,"user_tz":-540,"elapsed":9,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"execution":{"iopub.status.busy":"2023-12-05T00:11:45.266381Z","iopub.execute_input":"2023-12-05T00:11:45.266695Z","iopub.status.idle":"2023-12-05T00:11:45.289169Z","shell.execute_reply.started":"2023-12-05T00:11:45.266669Z","shell.execute_reply":"2023-12-05T00:11:45.288357Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### prepare test data","metadata":{"id":"CtMKG9bkj7hE"}},{"cell_type":"code","source":"import math\nimport os\nimport sys\nimport time\nfrom contextlib import contextmanager\nimport psutil\nimport shutil\nimport numpy as np\nimport polars as pl\nfrom tqdm import tqdm\n\n@contextmanager\ndef trace(title):\n    t0 = time.time()\n    p = psutil.Process(os.getpid())\n    m0 = p.memory_info().rss / 2.0**30\n    yield\n    m1 = p.memory_info().rss / 2.0**30\n    delta = m1 - m0\n    sign = \"+\" if delta >= 0 else \"-\"\n    delta = math.fabs(delta)\n    print(f\"[{m1:.1f}GB({sign}{delta:.1f}GB):{time.time() - t0:.1f}sec] {title} \", file=sys.stderr)\n\n\n# ============================================================================\n# Settings\n# ============================================================================\nSERIES_SCHEMA = {\n    \"series_id\": pl.Utf8,\n    \"step\": pl.UInt32,\n    \"anglez\": pl.Float32,\n    \"enmo\": pl.Float32,\n}\n\nFEATURE_NAMES = [\n    \"anglez\",\n    \"enmo\",\n    \"step\",\n    \"hour_sin\",\n    \"hour_cos\",\n]\n\nANGLEZ_MEAN = -8.810476\nANGLEZ_STD = 35.521877\nENMO_MEAN = 0.041315\nENMO_STD = 0.101829","metadata":{"id":"ktI3ViGs-rbu","executionInfo":{"status":"ok","timestamp":1701697280940,"user_tz":-540,"elapsed":9,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"execution":{"iopub.status.busy":"2023-12-05T00:11:45.291280Z","iopub.execute_input":"2023-12-05T00:11:45.291568Z","iopub.status.idle":"2023-12-05T00:11:45.305822Z","shell.execute_reply.started":"2023-12-05T00:11:45.291544Z","shell.execute_reply":"2023-12-05T00:11:45.304953Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def to_coord(x: pl.Expr, max_: int, name: str) -> list[pl.Expr]:\n    rad = 2 * np.pi * (x % max_) / max_\n    x_sin = rad.sin()\n    x_cos = rad.cos()\n\n    return [x_sin.alias(f\"{name}_sin\"), x_cos.alias(f\"{name}_cos\")]\n\n\ndef add_feature(series_df: pl.DataFrame) -> pl.DataFrame:\n    series_df = (\n        series_df.with_row_count(\"step\")\n        .with_columns(\n            *to_coord(pl.col(\"timestamp\").dt.hour(), 24, \"hour\"),\n            # *to_coord(pl.col(\"timestamp\").dt.month(), 12, \"month\"),\n            # *to_coord(pl.col(\"timestamp\").dt.minute(), 60, \"minute\"),\n            pl.col(\"step\") / pl.count(\"step\"),\n        )\n        .select(\"series_id\", *FEATURE_NAMES)\n    )\n    return series_df\n\ndef save_each_series(this_series_df: pl.DataFrame, columns: list[str], output_dir: Path):\n\n    output_dir.mkdir(parents=True, exist_ok=True)\n\n    for col_name in columns:\n        x = this_series_df.get_column(col_name).to_numpy(zero_copy_only=True)\n        np.save(output_dir / f\"{col_name}.npy\", x)","metadata":{"id":"Mok1Nyoz_V2a","executionInfo":{"status":"ok","timestamp":1701697280940,"user_tz":-540,"elapsed":9,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"execution":{"iopub.status.busy":"2023-12-05T00:11:45.306786Z","iopub.execute_input":"2023-12-05T00:11:45.307047Z","iopub.status.idle":"2023-12-05T00:11:45.320056Z","shell.execute_reply.started":"2023-12-05T00:11:45.307025Z","shell.execute_reply":"2023-12-05T00:11:45.319281Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"with trace(\"Load series\"):\n\n    series_lf = pl.scan_parquet(cfg.data_dir + f\"/{cfg.phase}_series.parquet\",\n                                low_memory=True,\n                                )\n    # preprocess\n    series_df = (\n        series_lf.with_columns(\n            pl.col(\"timestamp\").str.to_datetime(\"%Y-%m-%dT%H:%M:%S%z\"),\n            (pl.col(\"anglez\") - ANGLEZ_MEAN) / ANGLEZ_STD,\n            (pl.col(\"enmo\") - ENMO_MEAN) / ENMO_STD,\n        )\n        .select(\n            [\n                pl.col(\"series_id\"),\n                pl.col(\"anglez\"),\n                pl.col(\"enmo\"),\n                pl.col(\"timestamp\"),\n            ]\n        )\n        .collect(streaming=True)\n        .sort(by=[\"series_id\", \"timestamp\"])\n    )\n    n_unique = series_df.get_column(\"series_id\").n_unique()\n\nwith trace(\"Save features\"):\n\n    for series_id, this_series_df in tqdm(series_df.group_by(\"series_id\"), total=n_unique):\n\n        this_series_df = add_feature(this_series_df)\n\n        series_dir = Path(cfg.processed_dir) / cfg.phase / series_id  # type: ignore\n        save_each_series(this_series_df, FEATURE_NAMES, series_dir)","metadata":{"executionInfo":{"elapsed":8,"status":"ok","timestamp":1701697280940,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"},"user_tz":-540},"id":"kyYrAWX4-q70","outputId":"50cff380-d364-4f51-df10-14a0ab755c30","execution":{"iopub.status.busy":"2023-12-05T00:11:45.321180Z","iopub.execute_input":"2023-12-05T00:11:45.321585Z","iopub.status.idle":"2023-12-05T00:11:45.481860Z","shell.execute_reply.started":"2023-12-05T00:11:45.321552Z","shell.execute_reply":"2023-12-05T00:11:45.480989Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### common.py","metadata":{"id":"WGZIepgdj7hE"}},{"cell_type":"code","source":"def pad_if_needed(x: np.ndarray, max_len: int, pad_value: float = 0.0) -> np.ndarray:\n    if len(x) == max_len:\n        return x\n    num_pad = max_len - len(x)\n    n_dim = len(x.shape)\n\n    # padding\n    pad_widths = [(0, num_pad)] + [(0, 0) for _ in range(n_dim - 1)]\n\n    return np.pad(x, pad_width=pad_widths, mode=\"constant\", constant_values=pad_value)\n\n\ndef nearest_valid_size(input_size: int, downsample_rate: int) -> int:\n    \"\"\"\n    (x // hop_length) % 32 == 0\n    を満たすinput_sizeに最も近いxを返す\n    \"\"\"\n    while (input_size // downsample_rate) % 32 != 0:\n        input_size += 1\n        print(input_size)\n    assert (input_size // downsample_rate) % 32 == 0\n\n    return input_size\n\ndef random_crop(pos: int, duration: int, max_end) -> tuple[int, int]:\n    \"\"\"Randomly crops with duration length including pos.\n    However, 0<=start, end<=max_end\n    \"\"\"\n    start = random.randint(max(0, pos - duration), min(pos, max_end - duration))\n    end = start + duration\n    return start, end\n\ndef negative_sampling(this_event_df: pd.DataFrame, num_steps: int) -> int:\n    \"\"\"negative sampling\n\n    Args:\n        this_event_df (pd.DataFrame): event df\n        num_steps (int): number of steps in this series\n\n    Returns:\n        int: negative sample position\n    \"\"\"\n    # onsetとwakupを除いた範囲からランダムにサンプリング\n    positive_positions = set(this_event_df[[\"onset\", \"wakeup\"]].to_numpy().flatten().tolist())\n    negative_positions = list(set(range(num_steps)) - positive_positions)\n    return random.sample(negative_positions, 1)[0]\n\n\n# ref: https://www.kaggle.com/competitions/dfl-bundesliga-data-shootout/discussion/360236#2004730\ndef gaussian_kernel(length: int, sigma: int = 3) -> np.ndarray:\n    x = np.ogrid[-length : length + 1]\n    h = np.exp(-(x**2) / (2 * sigma * sigma))  # type: ignore\n    h[h < np.finfo(h.dtype).eps * h.max()] = 0\n    return h\n\ndef gaussian_label(label: np.ndarray, offset: int, sigma: int) -> np.ndarray:\n    num_events = label.shape[1]\n    # np.convolveは、NumPyライブラリで提供される関数の一つで、畳み込み演算（convolution operation）を実行する\n    for i in range(num_events):\n        label[:, i] = np.convolve(label[:, i], gaussian_kernel(offset, sigma), mode=\"same\")\n\n    return label\n","metadata":{"id":"hZ4qlJcdj7hF","executionInfo":{"status":"ok","timestamp":1701697280940,"user_tz":-540,"elapsed":5,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"execution":{"iopub.status.busy":"2023-12-05T00:11:45.483025Z","iopub.execute_input":"2023-12-05T00:11:45.483293Z","iopub.status.idle":"2023-12-05T00:11:45.497072Z","shell.execute_reply.started":"2023-12-05T00:11:45.483269Z","shell.execute_reply":"2023-12-05T00:11:45.496196Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### seg.py","metadata":{"id":"WorpQHcVj7hG"}},{"cell_type":"code","source":"class SegTestDataset(Dataset):\n    def __init__(\n        self,\n        cfg,\n        chunk_features: dict[str, np.ndarray],\n    ):\n        self.cfg = cfg\n        self.chunk_features = chunk_features\n        self.keys = list(chunk_features.keys())\n        self.num_features = len(cfg.features)\n        self.upsampled_num_frames = nearest_valid_size(\n            int(self.cfg.duration * self.cfg.upsample_rate), self.cfg.downsample_rate\n        )\n\n    def __len__(self):\n        return len(self.keys)\n\n    def __getitem__(self, idx):\n        key = self.keys[idx]\n        feature = self.chunk_features[key]\n        feature = torch.FloatTensor(feature.T).unsqueeze(0)  # (1, num_features, duration)\n        feature = resize(\n            feature,\n            size=[self.num_features, self.upsampled_num_frames],\n            antialias=False,\n        ).squeeze(0)\n\n        return {\n            \"key\": key,\n            \"feature\": feature,  # (num_features, duration)\n        }\n","metadata":{"id":"INsQiBm8TdnL","executionInfo":{"status":"ok","timestamp":1701697280941,"user_tz":-540,"elapsed":6,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"execution":{"iopub.status.busy":"2023-12-05T00:11:45.498123Z","iopub.execute_input":"2023-12-05T00:11:45.498384Z","iopub.status.idle":"2023-12-05T00:11:45.509111Z","shell.execute_reply.started":"2023-12-05T00:11:45.498361Z","shell.execute_reply":"2023-12-05T00:11:45.508344Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### datamodule.py","metadata":{"id":"DUwzvnFUj7hH"}},{"cell_type":"code","source":"from typing import Optional\nfrom tqdm import tqdm\n\ndef load_chunk_features(\n    duration: int,\n    feature_names: list[str],\n    series_ids: Optional[list[str]],\n    processed_dir: Path,\n    phase: str,\n) -> dict[str, np.ndarray]:\n    \"\"\"_summary_\n\n    Args:\n        duration (int): 各チャンクの長さ\n        feature_names (list[str]): _description_\n        series_ids (Optional[list[str]]): _description_\n        processed_dir (Path): _description_\n        phase (str): _description_\n\n    Returns:\n        dict[str, np.ndarray]:\n                key:series_id,\n                value:各行が各step, 各列が各特徴量\n    \"\"\"\n    features = {}\n\n    if series_ids is None:\n        series_ids = [series_dir.name for series_dir in (processed_dir / phase).glob(\"*\")]\n\n    for series_id in series_ids:\n        series_dir = processed_dir / phase / series_id\n        this_feature = []\n        for feature_name in feature_names:\n            this_feature.append(np.load(series_dir / f\"{feature_name}.npy\"))\n        this_feature = np.stack(this_feature, axis=1)\n        num_chunks = (len(this_feature) // duration) + 1\n        for i in range(num_chunks):\n            chunk_feature = this_feature[i * duration : (i + 1) * duration]\n            chunk_feature = pad_if_needed(chunk_feature, duration, pad_value=0)  # type: ignore\n            features[f\"{series_id}_{i:07}\"] = chunk_feature\n\n    return features\n","metadata":{"id":"bht5F2DyBh_M","executionInfo":{"status":"ok","timestamp":1701697280941,"user_tz":-540,"elapsed":6,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"execution":{"iopub.status.busy":"2023-12-05T00:11:45.510060Z","iopub.execute_input":"2023-12-05T00:11:45.510306Z","iopub.status.idle":"2023-12-05T00:11:45.523293Z","shell.execute_reply.started":"2023-12-05T00:11:45.510283Z","shell.execute_reply":"2023-12-05T00:11:45.522609Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### modelmodule.py","metadata":{"id":"CHtWDH0Qj7hI"}},{"cell_type":"code","source":"import torch.nn as nn\nfrom typing import Callable, Optional\n# ref: https://github.com/analokmaus/kaggle-g2net-public/tree/main/models1d_pytorch\nclass CNNSpectrogram(nn.Module):\n    def __init__(\n        self,\n        in_channels: int = 3,\n        # base_filters: int | tuple = 128,\n        base_filters = 128,\n        kernel_sizes: tuple = (32, 16, 4, 2),\n        stride: int = 4,\n        sigmoid: bool = False,\n        output_size: Optional[int] = None,\n        conv: Callable = nn.Conv1d,\n        reinit: bool = True,\n    ):\n        super().__init__()\n        self.out_chans = len(kernel_sizes)\n        self.out_size = output_size\n        self.sigmoid = sigmoid\n        if isinstance(base_filters, int):\n            base_filters = tuple([base_filters])\n        self.height = base_filters[-1]\n        self.spec_conv = nn.ModuleList()\n        for i in range(self.out_chans):\n            tmp_block = [\n                conv(\n                    in_channels,\n                    base_filters[0],\n                    kernel_size=kernel_sizes[i],\n                    stride=stride,\n                    padding=(kernel_sizes[i] - 1) // 2,\n                )\n            ]\n            if len(base_filters) > 1:\n                for j in range(len(base_filters) - 1):\n                    tmp_block = tmp_block + [\n                        nn.BatchNorm1d(base_filters[j]),\n                        nn.ReLU(inplace=True),\n                        conv(\n                            base_filters[j],\n                            base_filters[j + 1],\n                            kernel_size=kernel_sizes[i],\n                            stride=stride,\n                            padding=(kernel_sizes[i] - 1) // 2,\n                        ),\n                    ]\n                self.spec_conv.append(nn.Sequential(*tmp_block))\n            else:\n                self.spec_conv.append(tmp_block[0])\n\n        if self.out_size is not None:\n            self.pool = nn.AdaptiveAvgPool2d((None, self.out_size))\n\n        if reinit:\n            for m in self.modules():\n                if isinstance(m, nn.Conv1d):\n                    nn.init.kaiming_normal_(m.weight)\n                elif isinstance(m, nn.BatchNorm1d):\n                    nn.init.constant_(m.weight, 1)\n                    nn.init.constant_(m.bias, 0)\n\n    def forward(self, x: torch.Tensor) -> torch.Tensor:\n        \"\"\"Forward pass of the model.\n\n        Args:\n            x (_type_): (batch_size, in_channels, time_steps)\n\n        Returns:\n            _type_: (batch_size, out_chans, height, time_steps)\n        \"\"\"\n        # x: (batch_size, in_channels, time_steps)\n        out: list[torch.Tensor] = []\n        for i in range(self.out_chans):\n            out.append(self.spec_conv[i](x))\n        img = torch.stack(out, dim=1)  # (batch_size, out_chans, height, time_steps)\n        if self.out_size is not None:\n            img = self.pool(img)  # (batch_size, out_chans, height, out_size)\n        if self.sigmoid:\n            img = img.sigmoid()\n        return img","metadata":{"id":"JsoMD6PCj7hI","executionInfo":{"status":"ok","timestamp":1701697280941,"user_tz":-540,"elapsed":6,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"execution":{"iopub.status.busy":"2023-12-05T00:11:45.526872Z","iopub.execute_input":"2023-12-05T00:11:45.527129Z","iopub.status.idle":"2023-12-05T00:11:45.542788Z","shell.execute_reply.started":"2023-12-05T00:11:45.527106Z","shell.execute_reply":"2023-12-05T00:11:45.541973Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# ref: https://github.com/bamps53/kaggle-dfl-3rd-place-solution/blob/master/models/cnn_3d.py\nfrom functools import partial\nfrom typing import Optional\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\n\nclass SEModule(nn.Module):\n    def __init__(self, channel, reduction=8):\n        super().__init__()\n        self.avg_pool = nn.AdaptiveAvgPool1d(1)\n        self.fc = nn.Sequential(\n            nn.Linear(channel, channel // reduction, bias=False),\n            nn.ReLU(inplace=True),\n            nn.Linear(channel // reduction, channel, bias=False),\n            nn.Sigmoid(),\n        )\n\n    def forward(self, x):\n        (\n            b,\n            c,\n            _,\n        ) = x.size()\n        y = self.avg_pool(x).view(b, c)\n        y = self.fc(y).view(b, c, 1)\n        return x * y.expand_as(x)\n\n\nclass DoubleConv(nn.Module):\n    \"\"\"(convolution => [BN] => ReLU) * 2\"\"\"\n\n    def __init__(\n        self,\n        in_channels,\n        out_channels,\n        mid_channels=None,\n        norm=nn.BatchNorm1d,\n        se=False,\n        res=False,\n    ):\n        super().__init__()\n        self.res = res\n        if not mid_channels:\n            mid_channels = out_channels\n        if se:\n            non_linearity = SEModule(out_channels)\n        else:\n            non_linearity = nn.ReLU(inplace=True)\n        self.double_conv = nn.Sequential(\n            nn.Conv1d(in_channels, mid_channels, kernel_size=3, padding=1, bias=False),\n            norm(mid_channels),\n            nn.ReLU(inplace=True),\n            nn.Conv1d(mid_channels, out_channels, kernel_size=3, padding=1, bias=False),\n            norm(out_channels),\n            non_linearity,\n        )\n\n    def forward(self, x):\n        if self.res:\n            return x + self.double_conv(x)\n        else:\n            return self.double_conv(x)\n\n\nclass Down(nn.Module):\n    \"\"\"Downscaling with maxpool then double conv\"\"\"\n\n    def __init__(\n        self, in_channels, out_channels, scale_factor, norm=nn.BatchNorm1d, se=False, res=False\n    ):\n        super().__init__()\n        self.maxpool_conv = nn.Sequential(\n            nn.MaxPool1d(scale_factor),\n            DoubleConv(in_channels, out_channels, norm=norm, se=se, res=res),\n        )\n\n    def forward(self, x):\n        return self.maxpool_conv(x)\n\n\nclass Up(nn.Module):\n    \"\"\"Upscaling then double conv\"\"\"\n\n    def __init__(\n        self, in_channels, out_channels, bilinear=True, scale_factor=2, norm=nn.BatchNorm1d\n    ):\n        super().__init__()\n\n        # if bilinear, use the normal convolutions to reduce the number of channels\n        if bilinear:\n            self.up = nn.Upsample(scale_factor=scale_factor, mode=\"bilinear\", align_corners=True)\n            self.conv = DoubleConv(in_channels, out_channels, in_channels // 2, norm=norm)\n        else:\n            self.up = nn.ConvTranspose1d(\n                in_channels, in_channels // 2, kernel_size=scale_factor, stride=scale_factor\n            )\n            self.conv = DoubleConv(in_channels, out_channels, norm=norm)\n\n    def forward(self, x1, x2):\n        x1 = self.up(x1)\n        # input is CHW\n        diff = x2.size()[2] - x1.size()[2]\n        x1 = F.pad(x1, [diff // 2, diff - diff // 2])\n        x = torch.cat([x2, x1], dim=1)\n        return self.conv(x)\n\n\ndef create_layer_norm(channel, length):\n    return nn.LayerNorm([channel, length])\n\n\nclass UNet1DDecoder(nn.Module):\n    def __init__(\n        self,\n        n_channels: int,\n        n_classes: int,\n        duration: int,\n        bilinear: bool = True,\n        se: bool = False,\n        res: bool = False,\n        scale_factor: int = 2,\n        dropout: float = 0.2,\n    ):\n        super().__init__()\n        self.n_channels = n_channels\n        self.n_classes = n_classes\n        self.duration = duration\n        self.bilinear = bilinear\n        self.se = se\n        self.res = res\n        self.scale_factor = scale_factor\n\n        factor = 2 if bilinear else 1\n        self.inc = DoubleConv(\n            self.n_channels, 64, norm=partial(create_layer_norm, length=self.duration)\n        )\n        self.down1 = Down(\n            64, 128, scale_factor, norm=partial(create_layer_norm, length=self.duration // 2)\n        )\n        self.down2 = Down(\n            128, 256, scale_factor, norm=partial(create_layer_norm, length=self.duration // 4)\n        )\n        self.down3 = Down(\n            256, 512, scale_factor, norm=partial(create_layer_norm, length=self.duration // 8)\n        )\n        self.down4 = Down(\n            512,\n            1024 // factor,\n            scale_factor,\n            norm=partial(create_layer_norm, length=self.duration // 16),\n        )\n        self.up1 = Up(\n            1024,\n            512 // factor,\n            bilinear,\n            scale_factor,\n            norm=partial(create_layer_norm, length=self.duration // 8),\n        )\n        self.up2 = Up(\n            512,\n            256 // factor,\n            bilinear,\n            scale_factor,\n            norm=partial(create_layer_norm, length=self.duration // 4),\n        )\n        self.up3 = Up(\n            256,\n            128 // factor,\n            bilinear,\n            scale_factor,\n            norm=partial(create_layer_norm, length=self.duration // 2),\n        )\n        self.up4 = Up(\n            128, 64, bilinear, scale_factor, norm=partial(create_layer_norm, length=self.duration)\n        )\n\n        self.cls = nn.Sequential(\n            nn.Conv1d(64, 64, kernel_size=3, padding=1),\n            nn.ReLU(),\n            nn.Conv1d(64, self.n_classes, kernel_size=1, padding=0),\n            nn.Dropout(dropout),\n        )\n        self.loss_fn = nn.BCEWithLogitsLoss()\n\n    def forward(\n        self, x: torch.Tensor, labels: Optional[torch.Tensor] = None\n    ) -> dict[str, Optional[torch.Tensor]]:\n        \"\"\"Forward\n\n        Args:\n            x (torch.Tensor): (batch_size, n_channels, n_timesteps)\n\n        Returns:\n            torch.Tensor: (batch_size, n_timesteps, n_classes)\n        \"\"\"\n\n        # 1D U-Net\n        x1 = self.inc(x)\n        x2 = self.down1(x1)\n        x3 = self.down2(x2)\n        x4 = self.down3(x3)\n        x = self.down4(x4)\n        x = self.up1(x, x4)\n        x = self.up2(x, x3)\n        x = self.up3(x, x2)\n        x = self.up4(x, x1)\n\n        # classifier\n        logits = self.cls(x)  # (batch_size, n_classes, n_timesteps)\n        return logits.transpose(1, 2)  # (batch_size, n_timesteps, n_classes)\n","metadata":{"id":"tTD3Zbc4j7hI","executionInfo":{"status":"ok","timestamp":1701697280941,"user_tz":-540,"elapsed":6,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"execution":{"iopub.status.busy":"2023-12-05T00:11:45.544074Z","iopub.execute_input":"2023-12-05T00:11:45.544346Z","iopub.status.idle":"2023-12-05T00:11:45.576277Z","shell.execute_reply.started":"2023-12-05T00:11:45.544307Z","shell.execute_reply":"2023-12-05T00:11:45.575470Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_rand_1dbbox(n_timesteps: int, lam: float) -> tuple[int, int]:\n    \"\"\"Get random 1D bounding box.\n\n    Args:\n        n_timesteps (int): Number of timesteps.\n        lam (float): Lambda value.\n\n    Returns:\n        tuple[int, int]: (start, end) of the bounding box.\n    \"\"\"\n    cut_rat = np.sqrt(1.0 - lam)\n    cut_len = int(n_timesteps * cut_rat)\n\n    start = np.random.randint(0, n_timesteps - cut_len)\n    end = start + cut_len\n\n    return start, end\n\nclass Cutmix:\n    def __init__(self, alpha: float = 0.4):\n        self.alpha = alpha\n\n    def __call__(\n        self, imgs: torch.Tensor, labels: torch.Tensor\n    ) -> tuple[torch.Tensor, torch.Tensor]:\n        \"\"\"Cutmix augmentation.\n\n        Args:\n            imgs (torch.Tensor): (batch_size, n_channels, n_timesteps)\n            labels (torch.Tensor): (batch_size, n_timesteps, n_classes)\n\n        Returns:\n            tuple[torch.Tensor]: mixed_imgs (batch_size, n_channels, n_timesteps)\n                                 mixed_labels (batch_size, n_timesteps, n_classes)\n        \"\"\"\n        batch_size = imgs.size(0)\n        idx = torch.randperm(batch_size)\n\n        shuffled_imgs = imgs[idx]\n        shuffled_labels = labels[idx]\n\n        lam = np.random.beta(self.alpha, self.alpha)\n        start, end = get_rand_1dbbox(imgs.size(2), lam)\n\n        mixed_imgs = torch.concatenate(\n            [imgs[:, :, :start], shuffled_imgs[:, :, start:end], imgs[:, :, end:]], dim=2\n        )\n        mixed_labels = torch.concatenate(\n            [labels[:, :start, :], shuffled_labels[:, start:end, :], labels[:, end:, :]], dim=1\n        )\n\n        return mixed_imgs, mixed_labels\n\nclass Mixup:\n    def __init__(self, alpha: float = 0.4):\n        self.alpha = alpha\n\n    def __call__(\n        self, imgs: torch.Tensor, labels: torch.Tensor\n    ) -> tuple[torch.Tensor, torch.Tensor]:\n        \"\"\"Mixup augmentation.\n\n        Args:\n            imgs (torch.Tensor): (batch_size, n_channels, n_timesteps)\n            labels (torch.Tensor): (batch_size, n_timesteps, n_classes)\n\n        Returns:\n            tuple[torch.Tensor]: mixed_imgs (batch_size, n_channels, n_timesteps)\n                                 mixed_labels (batch_size, n_timesteps, n_classes)\n        \"\"\"\n        batch_size = imgs.size(0)\n        idx = torch.randperm(batch_size)\n        lam = np.random.beta(self.alpha, self.alpha)\n\n        mixed_imgs: torch.Tensor = lam * imgs + (1 - lam) * imgs[idx]\n        mixed_labels: torch.Tensor = lam * labels + (1 - lam) * labels[idx]\n\n        return mixed_imgs, mixed_labels\n","metadata":{"id":"PXcRz6nFj7hJ","executionInfo":{"status":"ok","timestamp":1701697280941,"user_tz":-540,"elapsed":5,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"execution":{"iopub.status.busy":"2023-12-05T00:11:45.577971Z","iopub.execute_input":"2023-12-05T00:11:45.578360Z","iopub.status.idle":"2023-12-05T00:11:45.592121Z","shell.execute_reply.started":"2023-12-05T00:11:45.578326Z","shell.execute_reply":"2023-12-05T00:11:45.591268Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from abc import abstractmethod\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nimport torch\nimport torch.nn as nn\n\n@dataclass\nclass ModelOutput:\n    logits: torch.Tensor\n    loss: Optional[torch.Tensor] = None\n    preds: Optional[torch.Tensor] = None\n    labels: Optional[torch.Tensor] = None\n\nclass BaseModel(nn.Module):\n    def __init__(self):\n        super().__init__()\n\n    def forward(\n        self,\n        x: torch.Tensor,\n        labels: Optional[torch.Tensor] = None,\n        do_mixup: bool = False,\n        do_cutmix: bool = False,\n    ) -> ModelOutput:\n        \"\"\"Forward pass of the model.\n\n        Args:\n            x (torch.Tensor): (batch_size, n_channels, n_timesteps)\n            labels (Optional[torch.Tensor], optional): (batch_size, n_timesteps, n_classes)\n            do_mixup (bool, optional): Defaults to False.\n            do_cutmix (bool, optional):  Defaults to False.\n\n        Returns:\n            ModelOutput: model output\n        \"\"\"\n        if labels is not None:\n            logits, labels = self._forward(x, labels, do_mixup, do_cutmix)\n            loss = self.loss_fn(logits, labels)\n            return ModelOutput(logits=logits, loss=loss, labels=labels)\n        else:\n            logits = self._forward(x, labels=None, do_mixup=False, do_cutmix=False)\n            if isinstance(logits, torch.Tensor):\n                return ModelOutput(logits=logits)\n            else:\n                raise ValueError(\"logits must be a torch.Tensor\")\n\n    @torch.no_grad()\n    def predict(\n        self,\n        x: torch.Tensor,\n        org_duration: int,\n        labels: Optional[torch.Tensor] = None,\n    ) -> ModelOutput:\n        output = self.forward(x, labels, False, False)\n        preds = self._logits_to_proba_per_step(output.logits, org_duration)\n        output.preds = preds\n\n        if labels is not None:\n            labels = self._correct_labels(labels, org_duration)\n            output.labels = labels\n\n        return output\n\n    @abstractmethod\n    def _forward(\n        self,\n        x: torch.Tensor,\n        labels: Optional[torch.Tensor] = None,\n        do_mixup: bool = False,\n        do_cutmix: bool = False,\n    ):\n    # ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:\n        \"\"\"Forward pass of the model.\n\n        Args:\n            x (torch.Tensor): (batch_size, n_channels, n_timesteps)\n            labels (Optional[torch.Tensor], optional): (batch_size, n_timesteps, n_classes)\n            do_mixup (bool, optional): Defaults to False.\n            do_cutmix (bool, optional):  Defaults to False.\n\n        Returns:\n            torch.Tensor | tuple[torch.Tensor, torch.Tensor]: logits or (logits, labels)\n        \"\"\"\n        raise NotImplementedError\n\n    @abstractmethod\n    def _logits_to_proba_per_step(\n        self,\n        logits: torch.Tensor,\n        org_duration: int,\n    ) -> torch.Tensor:\n        \"\"\"Convert logits to probabilities per step.\n\n        Args:\n            logits (torch.Tensor): (batch_size, n_timesteps, n_classes)\n            org_duration (int): original duration in seconds\n\n        Returns:\n            torch.Tensor: (batch_size, org_duration, n_classes)\n        \"\"\"\n        raise NotImplementedError\n\n    @abstractmethod\n    def _correct_labels(\n        self,\n        labels: torch.Tensor,\n        org_duration: int,\n    ) -> torch.Tensor:\n        \"\"\"Correct labels to match the output of the model.\n\n        Args:\n            labels (torch.Tensor): (batch_size, org_duration, n_classes)\n            org_duration (int): original duration in seconds\n\n        Returns:\n            torch.Tensor: (batch_size, org_duration, n_classes)\n        \"\"\"\n        raise NotImplementedError\n","metadata":{"id":"QDFeeLR2j7hJ","executionInfo":{"status":"ok","timestamp":1701697280941,"user_tz":-540,"elapsed":5,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"execution":{"iopub.status.busy":"2023-12-05T00:11:45.593270Z","iopub.execute_input":"2023-12-05T00:11:45.593570Z","iopub.status.idle":"2023-12-05T00:11:45.609466Z","shell.execute_reply.started":"2023-12-05T00:11:45.593544Z","shell.execute_reply":"2023-12-05T00:11:45.608621Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from typing import Optional\n\nimport segmentation_models_pytorch as smp\nimport torch\nimport torch.nn as nn\nfrom torchvision.transforms.functional import resize\n\nclass Spec2DCNN(BaseModel):\n    def __init__(\n        self,\n        feature_extractor: nn.Module,\n        decoder: nn.Module,\n        encoder_name: str,\n        in_channels: int,\n        encoder_weights: Optional[str] = None,\n        mixup_alpha: float = 0.5,\n        cutmix_alpha: float = 0.5,\n    ):\n        super().__init__()\n        self.feature_extractor = feature_extractor\n        self.encoder = smp.Unet(\n            encoder_name=encoder_name,\n            encoder_weights=encoder_weights,\n            in_channels=in_channels,\n            classes=1,\n        )\n        self.decoder = decoder\n        self.mixup = Mixup(mixup_alpha)\n        self.cutmix = Cutmix(cutmix_alpha)\n        self.loss_fn = nn.BCEWithLogitsLoss()\n\n    def _forward(\n        self,\n        x: torch.Tensor,\n        labels: Optional[torch.Tensor] = None,\n        do_mixup: bool = False,\n        do_cutmix: bool = False,\n    # ) -> torch.Tensor | tuple[torch.Tensor, torch.Tensor]:\n    ):\n        x = self.feature_extractor(x)  # (batch_size, n_channels, height, n_timesteps)\n\n        if do_mixup and labels is not None:\n            x, labels = self.mixup(x, labels)\n        if do_cutmix and labels is not None:\n            x, labels = self.cutmix(x, labels)\n\n        x = self.encoder(x).squeeze(1)  # (batch_size, height, n_timesteps)\n        logits = self.decoder(x)  # (batch_size, n_timesteps, n_classes)\n\n        if labels is not None:\n            return logits, labels\n        else:\n            return logits\n\n    def _logits_to_proba_per_step(self, logits: torch.Tensor, org_duration: int) -> torch.Tensor:\n        preds = logits.sigmoid()\n        return resize(preds, size=[org_duration, preds.shape[-1]], antialias=False)[:, :, [1, 2]]\n\n    def _correct_labels(self, labels: torch.Tensor, org_duration: int) -> torch.Tensor:\n        return resize(labels, size=[org_duration, labels.shape[-1]], antialias=False)[:, :, [1, 2]]\n","metadata":{"id":"KOyPjMJUj7hJ","executionInfo":{"status":"ok","timestamp":1701697280941,"user_tz":-540,"elapsed":5,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"execution":{"iopub.status.busy":"2023-12-05T00:11:45.610884Z","iopub.execute_input":"2023-12-05T00:11:45.611214Z","iopub.status.idle":"2023-12-05T00:11:46.962222Z","shell.execute_reply.started":"2023-12-05T00:11:45.611181Z","shell.execute_reply":"2023-12-05T00:11:46.961428Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\ndef get_model(\n    cfg,\n    feature_dim: int,\n    n_classes: int,\n    num_timesteps: int,\n    test: bool = False,\n):\n    # model: BaseModel\n    # if cfg.model.name == \"Spec2DCNN\":\n\n    # feature_extractor = get_feature_extractor(\n    #     cfg.feature_extractor, feature_dim, num_timesteps\n    # )\n    feature_extractor = CNNSpectrogram(\n        in_channels=feature_dim,\n        output_size=num_timesteps,\n        # **cfg.params\n        base_filters=cfg.base_filters,\n        kernel_sizes=cfg.kernel_sizes,\n        stride=cfg.downsample_rate,\n        sigmoid=cfg.sigmoid,\n        reinit=cfg.reinit,\n    )\n\n    # decoder = get_decoder(cfg.decoder, feature_extractor.height, n_classes, num_timesteps)\n    decoder = UNet1DDecoder(\n            n_channels=feature_extractor.height,\n            n_classes=n_classes,\n            duration=num_timesteps,\n            # **cfg.params,\n            bilinear=False, # 元のyamlファイルより\n        )\n\n    if test:\n        cfg.encoder_weights = None\n    model = Spec2DCNN(\n        feature_extractor=feature_extractor,\n        decoder=decoder,\n        in_channels=feature_extractor.out_chans,\n        mixup_alpha=cfg.mixup_alpha,\n        cutmix_alpha=cfg.cutmix_alpha,\n        encoder_name=cfg.encoder_name, # yamlファイルより\n        encoder_weights=cfg.encoder_weights, # yamlファイルより\n    )\n\n    return model","metadata":{"id":"YBddTcU0j7hJ","executionInfo":{"status":"ok","timestamp":1701697282091,"user_tz":-540,"elapsed":12,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"execution":{"iopub.status.busy":"2023-12-05T00:11:46.963313Z","iopub.execute_input":"2023-12-05T00:11:46.963615Z","iopub.status.idle":"2023-12-05T00:11:46.971298Z","shell.execute_reply.started":"2023-12-05T00:11:46.963590Z","shell.execute_reply":"2023-12-05T00:11:46.970227Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from scipy.signal import find_peaks\n\ndef post_process_for_seg(\n    keys: list[str], preds: np.ndarray, score_th: float = 0.01, distance: int = 5000\n) -> pl.DataFrame:\n    \"\"\"make submission dataframe for segmentation task\n\n    Args:\n        keys (list[str]): list of keys. key is \"{series_id}_{chunk_id}\"\n        preds (np.ndarray): (num_series * num_chunks, duration, 2)\n        score_th (float, optional): threshold for score. Defaults to 0.5.\n\n    Returns:\n        pl.DataFrame: submission dataframe\n    \"\"\"\n    series_ids = np.array(list(map(lambda x: x.split(\"_\")[0], keys)))\n    unique_series_ids = np.unique(series_ids)\n\n    records = []\n    for series_id in unique_series_ids:\n        series_idx = np.where(series_ids == series_id)[0]\n        this_series_preds = preds[series_idx].reshape(-1, 2)\n\n        for i, event_name in enumerate([\"onset\", \"wakeup\"]):\n            this_event_preds = this_series_preds[:, i]\n            steps = find_peaks(this_event_preds, height=score_th, distance=distance)[0]\n            scores = this_event_preds[steps]\n\n            for step, score in zip(steps, scores):\n                records.append(\n                    {\n                        \"series_id\": series_id,\n                        \"step\": step,\n                        \"event\": event_name,\n                        \"score\": score,\n                    }\n                )\n\n    if len(records) == 0:  # 一つも予測がない場合はdummyを入れる\n        records.append(\n            {\n                \"series_id\": series_id,\n                \"step\": 0,\n                \"event\": \"onset\",\n                \"score\": 0,\n            }\n        )\n\n    sub_df = pl.DataFrame(records).sort(by=[\"series_id\", \"step\"])\n    row_ids = pl.Series(name=\"row_id\", values=np.arange(len(sub_df)))\n    sub_df = sub_df.with_columns(row_ids).select([\"row_id\", \"series_id\", \"step\", \"event\", \"score\"])\n    return sub_df","metadata":{"id":"kngX8FDbj7hJ","executionInfo":{"status":"ok","timestamp":1701697282091,"user_tz":-540,"elapsed":11,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"execution":{"iopub.status.busy":"2023-12-05T00:11:46.972481Z","iopub.execute_input":"2023-12-05T00:11:46.972831Z","iopub.status.idle":"2023-12-05T00:11:47.248398Z","shell.execute_reply.started":"2023-12-05T00:11:46.972806Z","shell.execute_reply":"2023-12-05T00:11:47.247530Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### inference","metadata":{"id":"KbG4zRnk-Fzn"}},{"cell_type":"code","source":"def load_model(cfg, model_path):\n    num_timesteps = nearest_valid_size(int(cfg.duration * cfg.upsample_rate), cfg.downsample_rate)\n    model = get_model(\n        cfg,\n        feature_dim=len(cfg.features),\n        n_classes=len(cfg.labels),\n        num_timesteps=num_timesteps // cfg.downsample_rate,\n        test=True,\n    )\n\n    # load weights\n    # if cfg.weight is not None:\n    # weight_path = (model_path)\n    model.load_state_dict(torch.load(model_path))\n    print('load weight from \"{}\"'.format(model_path))\n\n    return model","metadata":{"id":"1AukZkyuya6v","executionInfo":{"status":"ok","timestamp":1701697282091,"user_tz":-540,"elapsed":10,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"execution":{"iopub.status.busy":"2023-12-05T00:11:47.249447Z","iopub.execute_input":"2023-12-05T00:11:47.249720Z","iopub.status.idle":"2023-12-05T00:11:47.255759Z","shell.execute_reply.started":"2023-12-05T00:11:47.249695Z","shell.execute_reply":"2023-12-05T00:11:47.254788Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_test_dataloader(cfg):\n    \"\"\"get test dataloader\n\n    Args:\n        cfg (DictConfig): config\n\n    Returns:\n        DataLoader: test dataloader\n    \"\"\"\n    feature_dir = Path(cfg.processed_dir) / cfg.phase\n    series_ids = [x.name for x in feature_dir.glob(\"*\")]\n    chunk_features = load_chunk_features(\n        duration=cfg.duration,\n        feature_names=cfg.features,\n        series_ids=series_ids,\n        processed_dir=Path(cfg.processed_dir),\n        phase=cfg.phase,\n    )\n    # test_dataset = get_test_ds(cfg, chunk_features=chunk_features)\n    test_dataset = SegTestDataset(cfg=cfg, chunk_features=chunk_features)\n    test_dataloader = DataLoader(\n        test_dataset,\n        batch_size=cfg.batch_size,\n        shuffle=False,\n        num_workers=cfg.num_workers,\n        pin_memory=True,\n        drop_last=False,\n    )\n    return test_dataloader","metadata":{"id":"v20EeOmRsIsW","executionInfo":{"status":"ok","timestamp":1701697282092,"user_tz":-540,"elapsed":11,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"execution":{"iopub.status.busy":"2023-12-05T00:11:47.257356Z","iopub.execute_input":"2023-12-05T00:11:47.257704Z","iopub.status.idle":"2023-12-05T00:11:47.270259Z","shell.execute_reply.started":"2023-12-05T00:11:47.257673Z","shell.execute_reply":"2023-12-05T00:11:47.269395Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from torch.utils.data import DataLoader\n\ndef inference(duration, loader, model, device, use_amp) -> tuple[list[str], np.ndarray]:\n\n    model = model.to(device)\n    model.eval()\n\n    preds = []\n    keys = []\n    for batch in tqdm(loader, desc=\"inference\"):\n        with torch.no_grad():\n            with torch.cuda.amp.autocast(enabled=use_amp):\n                x = batch[\"feature\"].to(device)\n                output = model.predict(\n                    x,\n                    org_duration=duration,\n                )\n            if output.preds is None:\n                raise ValueError(\"output.preds is None\")\n            else:\n                key = batch[\"key\"]\n                preds.append(output.preds.detach().cpu().numpy())\n                keys.extend(key)\n\n    preds = np.concatenate(preds)\n\n    return keys, preds  # type: ignore","metadata":{"id":"6XUhuE3R-Fzo","executionInfo":{"status":"ok","timestamp":1701697282092,"user_tz":-540,"elapsed":10,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"execution":{"iopub.status.busy":"2023-12-05T00:11:47.271494Z","iopub.execute_input":"2023-12-05T00:11:47.271826Z","iopub.status.idle":"2023-12-05T00:11:47.282849Z","shell.execute_reply.started":"2023-12-05T00:11:47.271795Z","shell.execute_reply":"2023-12-05T00:11:47.282021Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def make_submission(\n    keys: list[str], preds: np.ndarray, score_th, distance\n) -> pl.DataFrame:\n    sub_df = post_process_for_seg(\n        keys,\n        preds,  # type: ignore\n        score_th=score_th,\n        distance=distance,  # type: ignore\n    )\n\n    return sub_df","metadata":{"id":"Q6CJah5N-Fzo","executionInfo":{"status":"ok","timestamp":1701697282092,"user_tz":-540,"elapsed":10,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"execution":{"iopub.status.busy":"2023-12-05T00:11:47.283906Z","iopub.execute_input":"2023-12-05T00:11:47.284165Z","iopub.status.idle":"2023-12-05T00:11:47.295877Z","shell.execute_reply.started":"2023-12-05T00:11:47.284142Z","shell.execute_reply":"2023-12-05T00:11:47.295154Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import glob\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\nseed_everything(cfg.seed)\ntest_dataloader = get_test_dataloader(cfg)\n\npreds_list = []\n\nfor model_path in glob.glob(f\"{cfg.model_dir}/best_model*.pth\"):\n\n    model = load_model(cfg, model_path)\n    keys, preds = inference(cfg.duration, test_dataloader, model, device, use_amp=cfg.use_amp)\n\n    preds_list.append(preds)\n\npred_array = np.array(preds_list)\nmean_values = np.mean(pred_array, axis=0)\n\nsub_df = make_submission(\n    keys,\n    mean_values,\n    score_th=cfg.score_th,\n    distance=cfg.distance,\n)\nsub_df.write_csv(Path(cfg.sub_dir) / \"submission.csv\")","metadata":{"executionInfo":{"elapsed":2160,"status":"ok","timestamp":1701698254536,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"},"user_tz":-540},"id":"k02VoV_R-Fzo","outputId":"97c89a8a-ee8b-44bc-c7df-11911cff4474","execution":{"iopub.status.busy":"2023-12-05T00:11:47.297005Z","iopub.execute_input":"2023-12-05T00:11:47.297329Z","iopub.status.idle":"2023-12-05T00:12:09.268357Z","shell.execute_reply.started":"2023-12-05T00:11:47.297300Z","shell.execute_reply":"2023-12-05T00:12:09.267458Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# delete file, dir\nif is_kaggle_kernel():\n    !rm -rf /kaggle/working/test","metadata":{"id":"lRA-0zpqItd0","execution":{"iopub.status.busy":"2023-12-05T00:12:09.269756Z","iopub.execute_input":"2023-12-05T00:12:09.270069Z","iopub.status.idle":"2023-12-05T00:12:10.276444Z","shell.execute_reply.started":"2023-12-05T00:12:09.270041Z","shell.execute_reply":"2023-12-05T00:12:10.275347Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# remove predictions in periods with repetitive signals","metadata":{"id":"rfNFOX6IGIWu"}},{"cell_type":"code","source":"with trace(\"Load series\"):\n\n    series_lf = pl.scan_parquet(cfg.data_dir + f\"/{cfg.phase}_series.parquet\",\n                                low_memory=True,\n                                )\n    # preprocess\n    series_df = (\n        series_lf.with_columns(\n            pl.col(\"timestamp\").str.to_datetime(\"%Y-%m-%dT%H:%M:%S%z\"),\n        )\n        .select(\n            [\n                pl.col(\"series_id\"),\n                pl.col(\"anglez\"),\n                pl.col(\"enmo\"),\n                pl.col(\"timestamp\"),\n                pl.col(\"step\"),\n            ]\n        )\n        .collect(streaming=True)\n        .sort(by=[\"series_id\", \"timestamp\"])\n    )\n    n_unique = series_df.get_column(\"series_id\").n_unique()","metadata":{"id":"fNnCtEQhGL74","executionInfo":{"status":"ok","timestamp":1701266196359,"user_tz":-540,"elapsed":498,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"outputId":"9fc5fb9b-5232-4a97-f1af-a89bb90dc191","execution":{"iopub.status.busy":"2023-12-05T00:12:10.277914Z","iopub.execute_input":"2023-12-05T00:12:10.278215Z","iopub.status.idle":"2023-12-05T00:12:10.295605Z","shell.execute_reply.started":"2023-12-05T00:12:10.278187Z","shell.execute_reply":"2023-12-05T00:12:10.294692Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from datetime import timedelta\n\ndef get_time_filterlist(this_series_df):\n    \"\"\"\n    get timestamp separated by one day from the timestamp in the data\n    \"\"\"\n    start_date = this_series_df.get_column(\"timestamp\").min()\n    end_date = this_series_df.get_column(\"timestamp\").max()\n\n    if start_date > start_date.replace(hour=15, minute=0, second=0):\n        start_15_date = start_date + timedelta(days=1)\n    else:\n        start_15_date = this_series_df[\"timestamp\"].min()\n\n    start_15_date = start_15_date.replace(hour=15, minute=0, second=0)\n\n    time_filterlist = []\n    time_filterlist.append((start_date, start_15_date))\n\n    while start_15_date + timedelta(days=1) <= end_date:\n        time_filterlist.append((start_15_date, start_15_date + timedelta(days=1)))\n        start_15_date += timedelta(days=1)\n\n    time_filterlist.append((start_15_date, end_date))\n\n    return time_filterlist","metadata":{"id":"0P6zGQOXGL3t","execution":{"iopub.status.busy":"2023-12-05T00:12:10.296781Z","iopub.execute_input":"2023-12-05T00:12:10.297029Z","iopub.status.idle":"2023-12-05T00:12:10.305640Z","shell.execute_reply.started":"2023-12-05T00:12:10.297007Z","shell.execute_reply":"2023-12-05T00:12:10.304698Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tqdm import tqdm\nimport itertools\n\nignore_step_dict = {}\n\nfor series_id, this_series_df in tqdm(series_df.group_by(\"series_id\"), total=n_unique):\n\n    time_filterlist = get_time_filterlist(this_series_df)\n    anglez_list = []\n    enmo_list = []\n    step_list = []\n\n    for idx, time_filter in enumerate(time_filterlist[1:-1]):\n\n        start = time_filter[0]\n        end = time_filter[1]\n\n        filtered_df = this_series_df.filter((this_series_df['timestamp'] >= start) &\n            (this_series_df['timestamp'] < end))\n\n        step_from = filtered_df['step'].min()\n        step_to = filtered_df['step'].max()\n\n        anglez_list.append(filtered_df['anglez'].to_numpy()[::100].mean())\n        enmo_list.append(filtered_df['enmo'].to_numpy()[::100].mean())\n        step_list.append((step_from, step_to))\n\n    anglez_list = np.array(anglez_list)\n    enmo_list = np.array(enmo_list)\n\n    # 各数字の出現回数を計算\n    unique_numbers_anglez, counts_anglez = np.unique(anglez_list, return_counts=True)\n    unique_numbers_enmo, counts_enmo = np.unique(enmo_list, return_counts=True)\n\n    # 出現回数が2回以上の数値を抜き出す\n    dup_values_anglez = unique_numbers_anglez[np.where(counts_anglez >= 2)[0]]\n    dup_values_enmo = unique_numbers_enmo[np.where(counts_enmo >= 2)[0]]\n\n    # 数値に該当するindexを抜き出し、angle, enmo両方で該当するindexを抜き出す\n    indices_angle = np.where(np.isin(anglez_list, dup_values_anglez))[0]\n    indices_enmo = np.where(np.isin(enmo_list, dup_values_enmo))[0]\n\n    del_indices = list(set(list(indices_enmo)) and set(list(indices_angle)))\n    del_step_list = [step_list[i] for i in del_indices]\n\n    ignore_step_dict |= {series_id:del_step_list}","metadata":{"id":"BhRTEOOnGL1H","executionInfo":{"status":"ok","timestamp":1701266205666,"user_tz":-540,"elapsed":901,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"outputId":"b8d318f2-8ee3-4bd1-c4a8-707d7fc2446c","execution":{"iopub.status.busy":"2023-12-05T00:12:10.306748Z","iopub.execute_input":"2023-12-05T00:12:10.307027Z","iopub.status.idle":"2023-12-05T00:12:10.327723Z","shell.execute_reply.started":"2023-12-05T00:12:10.307004Z","shell.execute_reply":"2023-12-05T00:12:10.326836Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub_df = pd.DataFrame(sub_df)\nsub_df.columns = [\"row_id\",\"series_id\", \"step\",\"event\",\"score\"]\n\nfor series_id, del_step_list in tqdm(ignore_step_dict.items()):\n\n    if len(del_step_list) == 0:\n        continue\n\n    for del_step in del_step_list:\n\n        step_from = del_step[0]\n        step_to = del_step[1]\n        sub_df = sub_df.loc[~((sub_df['series_id']==series_id) & (sub_df['step'] >= step_from) & (sub_df['step'] <= step_to)), :]","metadata":{"id":"kWhTw-62URXV","executionInfo":{"status":"ok","timestamp":1701266267220,"user_tz":-540,"elapsed":352,"user":{"displayName":"Yasuhiro Tsuji (Yasuhiro Tsuji)","userId":"08096114667578433227"}},"outputId":"58d15f2c-2e80-4dd9-c351-8ad90acaa632","execution":{"iopub.status.busy":"2023-12-05T00:12:10.328899Z","iopub.execute_input":"2023-12-05T00:12:10.329172Z","iopub.status.idle":"2023-12-05T00:12:10.343157Z","shell.execute_reply.started":"2023-12-05T00:12:10.329148Z","shell.execute_reply":"2023-12-05T00:12:10.342363Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub_df.reset_index(drop=True, inplace=True)\nsub_df['row_id'] = sub_df.index","metadata":{"id":"u5FyLjE6WYBV","execution":{"iopub.status.busy":"2023-12-05T00:12:10.344395Z","iopub.execute_input":"2023-12-05T00:12:10.344714Z","iopub.status.idle":"2023-12-05T00:12:10.359635Z","shell.execute_reply.started":"2023-12-05T00:12:10.344690Z","shell.execute_reply":"2023-12-05T00:12:10.358759Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub_df.to_csv(Path(cfg.sub_dir)/ \"submission.csv\", index=False)","metadata":{"id":"kdAcK3ydNgLF","execution":{"iopub.status.busy":"2023-12-05T00:12:10.375538Z","iopub.execute_input":"2023-12-05T00:12:10.375862Z","iopub.status.idle":"2023-12-05T00:12:10.382039Z","shell.execute_reply.started":"2023-12-05T00:12:10.375831Z","shell.execute_reply":"2023-12-05T00:12:10.381211Z"},"trusted":true},"execution_count":null,"outputs":[]}]}