{"metadata":{"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":75445,"databundleVersionId":8308692,"sourceType":"competition"}],"dockerImageVersionId":30699,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true},"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.10.13"},"papermill":{"default_parameters":{},"duration":14966.62028,"end_time":"2024-04-19T10:31:41.968006","environment_variables":{},"exception":null,"input_path":"__notebook__.ipynb","output_path":"__notebook__.ipynb","parameters":{},"start_time":"2024-04-19T06:22:15.347726","version":"2.5.0"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## Library 설정 및 필요 함수 정의\n\n수정 필요 없음\n\n**find_wav_files**: 디렉토리 내에 있는 .wav 파일들을 리스트화하는 함수\n\n**set_seed**: 랜덤 시드 설정에 대한 함수","metadata":{"papermill":{"duration":0.004564,"end_time":"2024-04-19T06:22:18.234825","exception":false,"start_time":"2024-04-19T06:22:18.230261","status":"completed"},"tags":[]}},{"cell_type":"code","source":"import os\nimport random\nfrom pathlib import Path\nimport numpy as np\nimport torch\nfrom torch import Tensor\nimport torch.nn as nn\nimport torch.nn.functional as F\nfrom torch.utils.data import ConcatDataset, DataLoader\nimport torchaudio\nimport logging\nfrom copy import deepcopy\nimport csv\nfrom typing import Callable, List, Optional, Tuple, Union\nfrom collections import OrderedDict\n\n\n### Utils\n\ndef find_wav_files(path_to_dir: Union[Path, str]) -> Optional[List[Path]]:\n    \"\"\"Find all wav files in the directory and its subtree.\n\n    Args:\n        path_to_dir: Path top directory.\n    Returns:\n        List containing Path objects or None (nothing found).\n    \"\"\"\n    paths = list(sorted(Path(path_to_dir).glob(\"**/*.wav\")))\n\n    if len(paths) == 0:\n        return None\n    return paths\n\n\ndef set_seed(seed: int):\n    \"\"\"Fix PRNG seed for reproducable experiments.\n    \"\"\"\n    random.seed(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    if torch.cuda.is_available():\n        torch.cuda.manual_seed(seed)\n        torch.cuda.manual_seed_all(seed)\n        torch.backends.cudnn.deterministic = True\n        torch.backends.cudnn.benchmark = False\n    os.environ[\"PYTHONHASHSEED\"] = str(seed)","metadata":{"papermill":{"duration":4.63889,"end_time":"2024-04-19T06:22:22.878187","exception":false,"start_time":"2024-04-19T06:22:18.239297","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 데이터셋 관련 코드\n\n**AudioDataset**: 데이터셋 관련 코드로 .wav파일 리스트 수행하고 샘플레이트를 각 음성에 대해 동일하게 맞춰줌\n\n`__init__`: .wav파일 리스트 수행. 데이터셋이 real 음성 / fake 음성의 비율이 1:7이라 두 음성의 비율을 맞춰주기 위해 real 음성의 샘플 수를 7배 늘려줌 \n\n`__getitem__`: wav파일을 읽어서 tensor로 바꿔줌. 샘플레이트를 각 음성에 대해 동일하게 맞춰줌. tensor와 샘플레이트를 리턴\n\n**PadDataset**: 모든 음성의 길이를 4초로 맞춰줌. 음성이 4초보다 길 경우 0초~4초로 자르고, 4초보다 짧은 경우 반복 재생하여 4초로 맞춰줌. 미니 배치 단위로 처리하기 떄문에 미니 배치 내의 각 샘플들의 길이를 동일하게 맞춰주는 작업으로 볼 수 있음\n\n**load_dataset**: 학습 데이터셋 불러오는 함수\n\n**load_dataset_test**: 테스트 데이터셋 불러오는 함수","metadata":{"papermill":{"duration":0.004067,"end_time":"2024-04-19T06:22:22.887420","exception":false,"start_time":"2024-04-19T06:22:22.883353","status":"completed"},"tags":[]}},{"cell_type":"code","source":"class AudioDataset(torch.utils.data.Dataset):\n    def __init__(\n            self,\n            directory_or_path_list: Union[Union[str, Path], List[Union[str, Path]]],\n            sample_rate: int = 16_000,\n            normalize: bool = True,\n            real: str = 'real',\n    ) -> None:\n        super().__init__()\n\n        self.sample_rate = sample_rate\n        self.normalize = normalize\n        if real == 'real':\n            directory_or_path_list = directory_or_path_list * 7\n\n        if isinstance(directory_or_path_list, list):\n            paths = directory_or_path_list\n        elif isinstance(directory_or_path_list, Path) \\\n                or isinstance(directory_or_path_list, str):\n            directory = Path(directory_or_path_list)\n            if not directory.exists():\n                raise IOError(f\"Directory does not exists: {self.directory}\")\n\n            paths = find_wav_files(directory)\n            if paths is None:\n                raise IOError(\n                    f\"Directory did not contain wav files: {self.directory}\")\n        else:\n            raise TypeError(\n                f\"Supplied unsupported type for argument directory_or_path_list {type(directory_or_path_list)}!\")\n\n\n        self._paths = paths\n\n    def __getitem__(self, index: int) -> Tuple[torch.Tensor, int]:\n        path = self._paths[index]\n\n        waveform, sample_rate = torchaudio.load(path, normalize=self.normalize)\n\n        if sample_rate != self.sample_rate:\n            transform = torchaudio.transforms.Resample(sample_rate, self.sample_rate)\n            waveform = transform(waveform)\n\n\n        return waveform, sample_rate\n\n    def __len__(self) -> int:\n        return len(self._paths)\n\n\nclass PadDataset(torch.utils.data.Dataset):\n\n    def __init__(self, dataset: torch.utils.data.Dataset, cut: int = 64600, label=None):\n        self.dataset = dataset\n        self.cut = cut  # max 4 sec (ASVSpoof default)\n        self.label = label\n\n    def __getitem__(self, index):\n        waveform, sample_rate = self.dataset[index]\n        waveform = waveform.squeeze(0)\n        waveform_len = waveform.shape[0]\n        if waveform_len >= self.cut:\n            if self.label is None:\n                return waveform[:self.cut], sample_rate\n            else:\n                return waveform[:self.cut], sample_rate, self.label\n        # need to pad\n        num_repeats = int(self.cut / waveform_len)+1\n        padded_waveform = torch.tile(waveform, (1, num_repeats))[\n            :, :self.cut][0]\n\n        if self.label is None:\n            return padded_waveform, sample_rate\n        else:\n            return padded_waveform, sample_rate, self.label\n\n    def __len__(self):\n        return len(self.dataset)\n\n\n\n\ndef load_dataset(\n        path: Union[Path, str],\n        pad: bool = False,\n        train: str = 'train',\n        real: str = 'real',\n        label: Optional[int] = None,\n) -> Tuple[torch.utils.data.Dataset]:\n\n    cur_path = \"{}/{}/{}\".format(path,train,real)\n    \n    paths = find_wav_files(cur_path)\n    if paths is None:\n        raise IOError(f\"Could not load files from {path}!\")\n\n    LOGGER.info(f\"Loading data from {path}...!\")\n\n    train_dataset = AudioDataset(\n        paths, real=real)\n    if pad:\n        train_dataset = PadDataset(train_dataset, label=label)\n\n    return train_dataset\n\n\n\n\ndef load_dataset_test(\n        path: Union[Path, str],\n        pad: bool = True,\n) -> Tuple[torch.utils.data.Dataset]:\n\n\n    paths = find_wav_files(path)\n    if paths is None:\n        raise IOError(f\"Could not load files from {path}!\")\n\n\n    test_dataset = AudioDataset(\n        paths, real='fake')\n    if pad:\n        test_dataset = PadDataset(test_dataset, label=0)\n\n    return test_dataset","metadata":{"papermill":{"duration":0.037138,"end_time":"2024-04-19T06:22:22.928545","exception":false,"start_time":"2024-04-19T06:22:22.891407","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 모델 관련 코드\n\n**RawNet**: RawNet2 코드\n\n`__init__`: 구조 생성\n\n`__forward__`: 순전파 수행. 입력 음성신호 x에 대해서 fake/real에 대한 확률 값을 softmax()를 취하여 출력\n","metadata":{"papermill":{"duration":0.0056,"end_time":"2024-04-19T06:22:22.939997","exception":false,"start_time":"2024-04-19T06:22:22.934397","status":"completed"},"tags":[]}},{"cell_type":"code","source":"# raw_Net2\n\nclass SincConv(nn.Module):\n    @staticmethod\n    def to_mel(hz):\n        return 2595 * np.log10(1 + hz / 700)\n\n    @staticmethod\n    def to_hz(mel):\n        return 700 * (10 ** (mel / 2595) - 1)\n\n\n    def __init__(self, device,out_channels, kernel_size,in_channels=1,sample_rate=16000,\n                 stride=1, padding=0, dilation=1, bias=False, groups=1):\n\n        super(SincConv,self).__init__()\n\n        if in_channels != 1:\n            \n            msg = \"SincConv only support one input channel (here, in_channels = {%i})\" % (in_channels)\n            raise ValueError(msg)\n        \n        self.out_channels = out_channels\n        self.kernel_size = kernel_size\n        self.sample_rate=sample_rate\n\n        # Forcing the filters to be odd (i.e, perfectly symmetrics)\n        if kernel_size%2==0:\n            self.kernel_size=self.kernel_size+1\n\n        self.device=device   \n        self.stride = stride\n        self.padding = padding\n        self.dilation = dilation\n        \n        if bias:\n            raise ValueError('SincConv does not support bias.')\n        if groups > 1:\n            raise ValueError('SincConv does not support groups.')\n        \n        \n        # initialize filterbanks using Mel scale\n        NFFT = 512\n        f=int(self.sample_rate/2)*np.linspace(0,1,int(NFFT/2)+1)\n        fmel=self.to_mel(f)   # Hz to mel conversion\n        fmelmax=np.max(fmel)\n        fmelmin=np.min(fmel)\n        filbandwidthsmel=np.linspace(fmelmin,fmelmax,self.out_channels+1)\n        filbandwidthsf=self.to_hz(filbandwidthsmel)  # Mel to Hz conversion\n        self.mel=filbandwidthsf\n        self.hsupp=torch.arange(-(self.kernel_size-1)/2, (self.kernel_size-1)/2+1)\n        self.band_pass=torch.zeros(self.out_channels,self.kernel_size)\n    \n       \n        \n    def forward(self,x):\n        for i in range(len(self.mel)-1):\n            fmin=self.mel[i]\n            fmax=self.mel[i+1]\n            hHigh=(2*fmax/self.sample_rate)*np.sinc(2*fmax*self.hsupp/self.sample_rate)\n            hLow=(2*fmin/self.sample_rate)*np.sinc(2*fmin*self.hsupp/self.sample_rate)\n            hideal=hHigh-hLow\n            \n            self.band_pass[i,:]=Tensor(np.hamming(self.kernel_size))*Tensor(hideal)\n        \n        band_pass_filter=self.band_pass.to(self.device)\n\n        self.filters = (band_pass_filter).view(self.out_channels, 1, self.kernel_size)\n        \n        return F.conv1d(x, self.filters, stride=self.stride,\n                        padding=self.padding, dilation=self.dilation,\n                         bias=None, groups=1)\n\n\n        \nclass Residual_block(nn.Module):\n    def __init__(self, nb_filts, first = False):\n        super(Residual_block, self).__init__()\n        self.first = first\n        \n        if not self.first:\n            self.bn1 = nn.BatchNorm1d(num_features = nb_filts[0])\n        \n        self.lrelu = nn.LeakyReLU(negative_slope=0.3)\n        \n        self.conv1 = nn.Conv1d(in_channels = nb_filts[0],\n\t\t\tout_channels = nb_filts[1],\n\t\t\tkernel_size = 3,\n\t\t\tpadding = 1,\n\t\t\tstride = 1)\n        \n        self.bn2 = nn.BatchNorm1d(num_features = nb_filts[1])\n        self.conv2 = nn.Conv1d(in_channels = nb_filts[1],\n\t\t\tout_channels = nb_filts[1],\n\t\t\tpadding = 1,\n\t\t\tkernel_size = 3,\n\t\t\tstride = 1)\n        \n        if nb_filts[0] != nb_filts[1]:\n            self.downsample = True\n            self.conv_downsample = nn.Conv1d(in_channels = nb_filts[0],\n\t\t\t\tout_channels = nb_filts[1],\n\t\t\t\tpadding = 0,\n\t\t\t\tkernel_size = 1,\n\t\t\t\tstride = 1)\n            \n        else:\n            self.downsample = False\n        self.mp = nn.MaxPool1d(3)\n        \n    def forward(self, x):\n        identity = x\n        if not self.first:\n            out = self.bn1(x)\n            out = self.lrelu(out)\n        else:\n            out = x\n            \n        out = self.conv1(x)\n        out = self.bn2(out)\n        out = self.lrelu(out)\n        out = self.conv2(out)\n        \n        if self.downsample:\n            identity = self.conv_downsample(identity)\n            \n        out += identity\n        out = self.mp(out)\n        return out\n\n\nclass RawNet(nn.Module):\n    def __init__(self, d_args, device):\n        super(RawNet, self).__init__()\n\n        \n        self.device=device\n\n        self.Sinc_conv=SincConv(device=self.device,\n\t\t\tout_channels = d_args['filts'][0],\n\t\t\tkernel_size = d_args['first_conv'],\n                        in_channels = d_args['in_channels']\n        )\n        \n        self.first_bn = nn.BatchNorm1d(num_features = d_args['filts'][0])\n        self.selu = nn.SELU(inplace=True)\n        self.block0 = Residual_block(nb_filts = d_args['filts'][1], first = True)\n        self.block1 = Residual_block(nb_filts = d_args['filts'][1])\n        self.block2 = Residual_block(nb_filts = d_args['filts'][2])\n        d_args['filts'][2][0] = d_args['filts'][2][1]\n        self.block3 = Residual_block(nb_filts = d_args['filts'][2])\n        self.block4 = Residual_block(nb_filts = d_args['filts'][2])\n        self.block5 = Residual_block(nb_filts = d_args['filts'][2])\n        self.avgpool = nn.AdaptiveAvgPool1d(1)\n\n        self.fc_attention0 = self._make_attention_fc(in_features = d_args['filts'][1][-1],\n            l_out_features = d_args['filts'][1][-1])\n        self.fc_attention1 = self._make_attention_fc(in_features = d_args['filts'][1][-1],\n            l_out_features = d_args['filts'][1][-1])\n        self.fc_attention2 = self._make_attention_fc(in_features = d_args['filts'][2][-1],\n            l_out_features = d_args['filts'][2][-1])\n        self.fc_attention3 = self._make_attention_fc(in_features = d_args['filts'][2][-1],\n            l_out_features = d_args['filts'][2][-1])\n        self.fc_attention4 = self._make_attention_fc(in_features = d_args['filts'][2][-1],\n            l_out_features = d_args['filts'][2][-1])\n        self.fc_attention5 = self._make_attention_fc(in_features = d_args['filts'][2][-1],\n            l_out_features = d_args['filts'][2][-1])\n\n        self.bn_before_gru = nn.BatchNorm1d(num_features = d_args['filts'][2][-1])\n        self.gru = nn.GRU(input_size = d_args['filts'][2][-1],\n\t\t\thidden_size = d_args['gru_node'],\n\t\t\tnum_layers = d_args['nb_gru_layer'],\n\t\t\tbatch_first = True)\n\n        \n        self.fc1_gru = nn.Linear(in_features = d_args['gru_node'],\n\t\t\tout_features = d_args['nb_fc_node'])\n       \n        self.fc2_gru = nn.Linear(in_features = d_args['nb_fc_node'],\n\t\t\tout_features = d_args['nb_classes'],bias=True)\n\t\t\t\n       \n        self.sig = nn.Sigmoid()\n        \n    def forward(self, x, y = None):\n        \n        \n        nb_samp = x.shape[0]\n        len_seq = x.shape[1]\n        x=x.view(nb_samp,1,len_seq)\n        \n        x = self.Sinc_conv(x)    \n        x = F.max_pool1d(torch.abs(x), 3)\n        x = self.first_bn(x)\n        x =  self.selu(x)\n        \n        x0 = self.block0(x)\n        y0 = self.avgpool(x0).view(x0.size(0), -1) # torch.Size([batch, filter])\n        y0 = self.fc_attention0(y0)\n        y0 = self.sig(y0).view(y0.size(0), y0.size(1), -1)  # torch.Size([batch, filter, 1])\n        x = x0 * y0 + y0  # (batch, filter, time) x (batch, filter, 1)\n        \n\n        x1 = self.block1(x)\n        y1 = self.avgpool(x1).view(x1.size(0), -1) # torch.Size([batch, filter])\n        y1 = self.fc_attention1(y1)\n        y1 = self.sig(y1).view(y1.size(0), y1.size(1), -1)  # torch.Size([batch, filter, 1])\n        x = x1 * y1 + y1 # (batch, filter, time) x (batch, filter, 1)\n\n        x2 = self.block2(x)\n        y2 = self.avgpool(x2).view(x2.size(0), -1) # torch.Size([batch, filter])\n        y2 = self.fc_attention2(y2)\n        y2 = self.sig(y2).view(y2.size(0), y2.size(1), -1)  # torch.Size([batch, filter, 1])\n        x = x2 * y2 + y2 # (batch, filter, time) x (batch, filter, 1)\n\n        x3 = self.block3(x)\n        y3 = self.avgpool(x3).view(x3.size(0), -1) # torch.Size([batch, filter])\n        y3 = self.fc_attention3(y3)\n        y3 = self.sig(y3).view(y3.size(0), y3.size(1), -1)  # torch.Size([batch, filter, 1])\n        x = x3 * y3 + y3 # (batch, filter, time) x (batch, filter, 1)\n\n        x4 = self.block4(x)\n        y4 = self.avgpool(x4).view(x4.size(0), -1) # torch.Size([batch, filter])\n        y4 = self.fc_attention4(y4)\n        y4 = self.sig(y4).view(y4.size(0), y4.size(1), -1)  # torch.Size([batch, filter, 1])\n        x = x4 * y4 + y4 # (batch, filter, time) x (batch, filter, 1)\n\n        x5 = self.block5(x)\n        y5 = self.avgpool(x5).view(x5.size(0), -1) # torch.Size([batch, filter])\n        y5 = self.fc_attention5(y5)\n        y5 = self.sig(y5).view(y5.size(0), y5.size(1), -1)  # torch.Size([batch, filter, 1])\n        x = x5 * y5 + y5 # (batch, filter, time) x (batch, filter, 1)\n\n        x = self.bn_before_gru(x)\n        x = self.selu(x)\n        x = x.permute(0, 2, 1)     #(batch, filt, time) >> (batch, time, filt)\n        self.gru.flatten_parameters()\n        x, _ = self.gru(x)\n        x = x[:,-1,:]\n        x = self.fc1_gru(x)\n        x = self.fc2_gru(x).softmax(dim=1)\n        output = x\n        return output\n\n\n    def _make_attention_fc(self, in_features, l_out_features):\n        l_fc = []\n        l_fc.append(nn.Linear(in_features = in_features,\n\t\t\t        out_features = l_out_features))\n        return nn.Sequential(*l_fc)\n","metadata":{"papermill":{"duration":0.157638,"end_time":"2024-04-19T06:22:23.103191","exception":false,"start_time":"2024-04-19T06:22:22.945553","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 학습 관련 코드\n\n**GDTrainer**\n\n`train`\n\n학습, 검증(validation) 데이터로더 생성\n\n손실 함수는 교차 엔트로피(cross entropy) 이용\n\noptimizer는 Adam 이용\n\n각 epoch에 대한 학습 및 검증 관련 내용 포함: 각 epoch의 학습이 수행되면 검증 수행하여 각 에포크의 정확도 계산\n\n\n`test`\n\n검증 정확도가 가장 높은 모델을 불러와서 테스트 수행\n\n결과를 submission.csv에 저장","metadata":{"papermill":{"duration":0.003779,"end_time":"2024-04-19T06:22:23.111251","exception":false,"start_time":"2024-04-19T06:22:23.107472","status":"completed"},"tags":[]}},{"cell_type":"code","source":"class Trainer(object):\n    def __init__(self,\n                 epochs: int = 20,\n                 batch_size: int = 32,\n                 device: str = \"cpu\",\n                 optimizer_fn: Callable = torch.optim.Adam,\n                 optimizer_kwargs: dict = {\"lr\": 1e-3},\n                 ) -> None:\n        self.epochs = epochs\n        self.batch_size = batch_size\n        self.device = device\n        self.optimizer_fn = optimizer_fn\n        self.optimizer_kwargs = optimizer_kwargs\n        self.epoch_test_losses: List[float] = []\n\n\n\nclass GDTrainer(Trainer):\n    def train(self,\n              dataset_train: torch.utils.data.Dataset,\n              dataset_validation: torch.utils.data.Dataset,\n              model: torch.nn.Module,\n              model_dir: str,\n              ):\n\n        train_loader = DataLoader(\n            dataset_train, batch_size=self.batch_size, shuffle=True, drop_last=True, num_workers=4)\n        validation_loader = DataLoader(\n            dataset_validation, batch_size=self.batch_size, drop_last=True, num_workers=4)\n\n        criterion = torch.nn.CrossEntropyLoss()\n        optim = self.optimizer_fn(model.parameters(), **self.optimizer_kwargs)\n\n        best_model = None\n        best_acc = 0\n        for epoch in range(self.epochs):\n            running_loss = 0\n            num_correct = 0.0\n            num_total = 0.0\n            model.train()\n\n            for i, (batch_x, _, batch_y) in enumerate(train_loader):\n                batch_size = batch_x.size(0)\n                num_total += batch_size\n\n                batch_x = batch_x.to(self.device)\n                batch_y = batch_y.to(self.device)\n\n                batch_out = model(batch_x)\n                batch_loss = criterion(batch_out, batch_y)\n\n                _, batch_pred = batch_out.max(dim=1)\n                num_correct += (batch_pred == batch_y).sum(dim=0).item()\n\n\n                running_loss += (batch_loss.item() * batch_size)\n                \n                if i % (train_loader.__len__() // 20) == 0:\n                    cur_loss = batch_loss\n                    LOGGER.info(f\"[{epoch:04d}] {i}/{train_loader.__len__()}: {cur_loss}\")\n\n                optim.zero_grad()\n                batch_loss.backward()\n                optim.step()\n\n            running_loss /= num_total\n            train_accuracy = (num_correct/num_total)*100\n            \n            \n            num_correct = 0.0\n            num_total = 0.0\n            model.eval()\n            for batch_x, _, batch_y in validation_loader:\n\n                batch_size = batch_x.size(0)\n                num_total += batch_size\n                batch_x = batch_x.to(self.device)\n                batch_y = batch_y.to(self.device)\n\n                batch_out = model(batch_x)\n\n                _, batch_pred = batch_out.max(dim=1)\n                num_correct += (batch_pred == batch_y).sum(dim=0).item()\n\n            valid_acc = 100 * (num_correct / num_total)\n\n            if best_model is None or valid_acc > best_acc:\n                best_acc = valid_acc\n                best_model = deepcopy(model.state_dict())\n                save_model(model, model_dir)\n\n            LOGGER.info(\n                f\"[{epoch:04d}]: {running_loss} - train acc: {train_accuracy} - valid_acc: {valid_acc}\")\n\n        model.load_state_dict(best_model)\n        return model\n    \n    def test(self,\n              dataset_test: torch.utils.data.Dataset,\n              model: torch.nn.Module,\n              ):\n        model.eval()\n        test_loader = DataLoader(\n            dataset_test, batch_size=1, drop_last=False)\n        \n        f = open('submission.csv', 'w', newline='')\n        wr = csv.writer(f)\n        wr.writerow(['Id', 'Predicted'])\n\n        for i, (batch_x, _, batch_y) in enumerate(test_loader):\n                batch_x = batch_x.to(self.device)\n                batch_y = batch_y.to(self.device)\n                batch_out = model(batch_x)\n\n                _, batch_pred = batch_out.max(dim=1)\n\n                wr.writerow([i+1, batch_pred[0].item()])\n        f.close()\n","metadata":{"papermill":{"duration":0.026886,"end_time":"2024-04-19T06:22:23.142152","exception":false,"start_time":"2024-04-19T06:22:23.115266","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 메인 코드\n\n![image.png](attachment:97aaea77-a6ee-4277-bfec-0377f469a312.png)","metadata":{"papermill":{"duration":0.003651,"end_time":"2024-04-19T06:22:23.149784","exception":false,"start_time":"2024-04-19T06:22:23.146133","status":"completed"},"tags":[]},"attachments":{"97aaea77-a6ee-4277-bfec-0377f469a312.png":{"image/png":"iVBORw0KGgoAAAANSUhEUgAAAlgAAAHCCAMAAAFzqnfAAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAHjUExURf////Hx8d/f37+/vwAAAFBQUEpKSiAgIJCQkNfX1/v7+wICAn5+fk5OTqampuPj46CgoMfHxxAQEIiIiFpaWiYmJpOTkzQ0NA4ODt3d3W5ubufn54qKitXV1QgICD4+Pv39/QoKCjo6OkhISDAwMPPz80BAQEJCQoCAgJiYmAwMDOvr6wYGBu/v783NzVZWVnZ2djg4ONnZ2e3t7cXFxcHBwVxcXGxsbK2trfn5+cnJyff391JSUqurqx4eHkRERBwcHLu7u7e3t0ZGRrm5uXBwcCQkJCgoKM/Pz1hYWDIyMhYWFmZmZpGRkdvb2xISEhgYGHJycgQEBHR0dMPDw6WlpTY2Np2dnS4uLkxMTLGxsa+vrywsLFRUVIaGhoKCgqenp2hoaDw8PLOzs6ioqJubm729vZqampaWlpeXl+Hh4YyMjJSUlBQUFK6urnp6eiIiIpKSkunp6aqqqqmpqWBgYI6Ojl5eXpycnKGhofX19RoaGmpqatHR0SoqKnh4eHx8fIuLi42NjbW1tYeHh4SEhGRkZKOjo8vLy29vb2JiYqSkpJmZmdPT06ysrKKiop6enuXl5Z+fn7CwsH9/f5WVlY+Pj4WFhbS0tHt7e4mJiba2trKysri4uIGBgbq6ugAAABRqmAYAAAChdFJOU/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8AsaCChgAAAAlwSFlzAAASdAAAEnQB3mYfeAAAOZJJREFUeF7tnVmSszoMhXnwCuwHlxdhYAcU7yzGVWyDRd9zJJshnf5vz1P09RAChBghy7I8dcaJ4rpQN9+Gi70bnW5P+vIOnBu71K3YqDvey0ddx/hi3qGVA/4mPvmFmhQ+QgXG/V/hv/eS6uux8VYWfeE9bl2vb74fSZWDwNYuyo53MHVZL8jH+oOAagEIPiB9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"}}},{"cell_type":"code","source":"LOGGER = logging.getLogger(__name__)\n\n## Trainer\nRAW_NET_CONFIG = {\n    \"nb_samp\": 64600,\n    \"first_conv\": 1024,   # no. of filter coefficients\n    \"in_channels\": 1,  # no. of filters channel in residual blocks\n    \"filts\": [20, [20, 20], [20, 128], [128, 128]],\n    \"blocks\": [2, 4],\n    \"nb_fc_node\": 1024,\n    \"gru_node\": 1024,\n    \"nb_gru_layer\": 3,\n    \"nb_classes\": 2,\n}\n\n\n\ndef init_logger(log_file):\n    LOGGER.setLevel(logging.INFO)\n\n    # create file handler\n    fh = logging.FileHandler(log_file)\n\n    # create console handler\n    ch = logging.StreamHandler()\n\n    # create formatter and add it to the handlers\n    formatter = logging.Formatter(\n        '%(asctime)s - %(levelname)s - %(message)s')\n    fh.setFormatter(formatter)\n    ch.setFormatter(formatter)\n    # add the handlers to the logger\n    LOGGER.addHandler(fh)\n    LOGGER.addHandler(ch)\n\n\ndef save_model(\n        model: torch.nn.Module,\n        model_dir: Union[Path, str],\n) -> None:\n    torch.save(model.state_dict(),\n               f\"{model_dir}/best.pth\")\n\n\ndef train_raw_net(\n        real_training_distribution: Union[Path, str],\n        fake_training_distributions: List[Union[Path, str]],\n        batch_size: int,\n        epochs: int,\n        device: str,\n        test_dir: str,\n        model_dir: Optional[str] = None,\n) -> None:\n\n    LOGGER.info(\"Loading data...\")\n\n    \n\n    # real data\n    real_dataset_train = load_dataset(\n        real_training_distribution,\n        pad=True,\n        train='train',\n        real='real',\n        label=1,\n    )\n\n    real_dataset_validation = load_dataset(\n        real_training_distribution,\n        pad=True,\n        train='validation',\n        real='real',\n        label=1,\n    )\n\n    LOGGER.info(f\"Training {fake_training_distributions}\")\n    # fake data\n    fake_dataset_train = load_dataset(\n            fake_training_distributions,\n            pad=True,\n            train='train',\n            real='fake',\n            label=0,\n        )\n\n    fake_dataset_validation = load_dataset(\n            fake_training_distributions,\n            pad=True,\n            train='validation',\n            real='fake',\n            label=0,\n        )\n    \n    # test\n    dataset_test = load_dataset_test(\n            test_dir,\n            pad=True,\n        )\n\n\n    current_model = RawNet(deepcopy(RAW_NET_CONFIG), device).to(device)\n    data_train = ConcatDataset([real_dataset_train, fake_dataset_train])\n    data_validation = ConcatDataset([real_dataset_validation, fake_dataset_validation])\n\n\n    LOGGER.info(\n            f\"Training rawnet model on {len(data_train)} audio files.\")\n    \n    current_model = GDTrainer(\n            device=device,\n            batch_size=batch_size,\n            epochs=epochs,\n            optimizer_kwargs={\n                \"lr\": 0.0001,\n                \"weight_decay\": 0.0001,\n            }\n        ).train(\n            dataset_train=data_train,\n            dataset_validation=data_validation,\n            model=current_model,\n            model_dir=model_dir,\n        )\n\n\n    LOGGER.info(\"Training is done!\")\n\n    #Test\n    GDTrainer(\n            device=device,\n            batch_size=batch_size,\n            epochs=epochs,\n            optimizer_kwargs={\n                \"lr\": 0.0001,\n                \"weight_decay\": 0.0001,\n            }\n        ).test(\n            dataset_test=dataset_test,\n            model=current_model,\n        )\n    LOGGER.info(\"Testing is done!\")\n\n\ndef main():\n    # fix all seeds\n    set_seed(42)\n\n    init_logger(\"experiments.log\")\n\n    LOGGER.setLevel(logging.DEBUG)\n\n    #device = \"cpu\"\n    device = \"cuda\" \n    \n\n    db_dir = '/kaggle/input/jbnu-2024-ai-competitons/audio_split'\n    test_dir = '/kaggle/input/jbnu-2024-ai-competitons/audio_split/test'\n\n    batch_size = 128\n    epochs = 10\n\n    model_dir_path = \"trained_models\"\n    model_dir = Path(model_dir_path)\n    if not model_dir.exists():\n        model_dir.mkdir(parents=True)\n\n    train_raw_net(real_training_distribution=db_dir, fake_training_distributions=db_dir, device=device, batch_size=batch_size, epochs=epochs, test_dir=test_dir, model_dir=model_dir)\n\nif __name__ == \"__main__\":\n    main()\n","metadata":{"_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","papermill":{"duration":14955.879718,"end_time":"2024-04-19T10:31:39.033353","exception":false,"start_time":"2024-04-19T06:22:23.153635","status":"completed"},"tags":[],"trusted":true},"execution_count":null,"outputs":[]}]}