{"cells":[{"metadata":{},"cell_type":"markdown","source":"# Intro\nHello, there! We are a study group composed of Federal University of Technology - Parana students. \n\nThis notebook is the product of our attemps to solve the problems proposed by this competition.\n\nWe are currently reading and reimplementing code variants from many notebooks, mainly: \n\nWe aim to develop 3 models:\n* CNN using 5 secs audio segments\n* Sound Event Detection Models (based on https://www.kaggle.com/hidehisaarai1213/introduction-to-sound-event-detection ideas)\n* Efficient transformers model, based on Fast Autoregressive Transformers with linear attention (https://arxiv.org/abs/2006.16236)","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"## Imports","execution_count":null},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport matplotlib.pyplot as plt\nimport os\nimport math\n        \nimport torch\nimport torch.nn as nn\nfrom torch.utils import data\nimport torchaudio\nfrom torchaudio.transforms import Spectrogram, MelSpectrogram\nfrom torchaudio.transforms import TimeStretch, AmplitudeToDB, ComplexNorm, Resample\nfrom torchaudio.transforms import FrequencyMasking, TimeMasking\n\nfrom tqdm import tqdm\nfrom contextlib import contextmanager\nimport logging\nimport random\nimport time\nimport warnings\nfrom typing import Optional\nfrom fastprogress import progress_bar\nfrom contextlib import contextmanager\nfrom pathlib import Path\n\nfrom sklearn.model_selection import train_test_split\n\nfor dirname, _, filenames in os.walk('../input/birdsong-recognition/train_audio/'):\n    print(dirname)\n    \n%matplotlib inline\n\n#!pip install --upgrade wandb\n#!wandb login 24e6fa65bbc894434420c2093f2c2dc44f888051\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"class config():\n    batch_size         = 4\n    epochs             = 100\n    learning_rate      = 10e-3\n    target_sample_rate = 32000 # 32kHz\n    mel_num            = 128\n    max_len            = 60\n    frac               = 0.2\n    device             = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    \n    train_dir  = '../input/birdsong-recognition/train_audio/'\n    train_csv  = '../input/birdsong-recognition/train.csv'\n    test_dir   = '../input/birdsong-recognition/example_test_audio/'\n    test_csv   = '../input/birdsong-recognition/test.csv'","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Utils","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def set_seed(seed: int = 42):\n    random.seed(seed)\n    np.random.seed(seed)\n    os.environ[\"PYTHONHASHSEED\"] = str(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)  # type: ignore\n    torch.backends.cudnn.deterministic = True  # type: ignore\n    torch.backends.cudnn.benchmark = True  # type: ignore\n    \ndef get_logger(out_file=None):\n    logger = logging.getLogger()\n    formatter = logging.Formatter(\"%(asctime)s - %(levelname)s - %(message)s\")\n    logger.handlers = []\n    logger.setLevel(logging.INFO)\n\n    handler = logging.StreamHandler()\n    handler.setFormatter(formatter)\n    handler.setLevel(logging.INFO)\n    logger.addHandler(handler)\n\n    if out_file is not None:\n        fh = logging.FileHandler(out_file)\n        fh.setFormatter(formatter)\n        fh.setLevel(logging.INFO)\n        logger.addHandler(fh)\n    logger.info(\"logger set up\")\n    return logger\n\n@contextmanager\ndef timer(name: str, logger: Optional[logging.Logger] = None):\n    t0 = time.time()\n    msg = f\"[{name}] start\"\n    if logger is None:\n        print(msg)\n    else:\n        logger.info(msg)\n    yield\n\n    msg = f\"[{name}] done in {time.time() - t0:.2f} s\"\n    if logger is None:\n        print(msg)\n    else:\n        logger.info(msg)\n\nlogger = get_logger(\"main.log\");\nset_seed(1213);","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Looking at the data","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"waveform, sample_rate = torchaudio.load(\"../input/birdsong-recognition/train_audio/aldfly/XC134874.mp3\")\n\nplt.plot(waveform.t().numpy())\nplt.xlabel(\"time\")\nplt.ylabel(\"signal\");","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_csv = pd.read_csv('../input/birdsong-recognition/train.csv')\ntrain_csv.columns","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"mfcc = torchaudio.transforms.MelSpectrogram(sample_rate, n_fft=2**11, f_max=8000)(waveform)\nmfcc = torchaudio.transforms.AmplitudeToDB(top_db=80)(mfcc)\n\nplt.figure()\nplt.title(\"exsample mel Spectrogram\")\nplt.imshow(mfcc[0].detach().numpy()[::-1], cmap='magma',aspect=5);\nplt.xlabel(\"time\")\nplt.ylabel(\"mel scale\");","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Loading the data","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"os.listdir(config.train_dir)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_csv = pd.read_csv(config.test_csv)\ntest_csv","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"BIRD_CODE = {\n    'aldfly': 0, 'ameavo': 1, 'amebit': 2, 'amecro': 3, 'amegfi': 4,\n    'amekes': 5, 'amepip': 6, 'amered': 7, 'amerob': 8, 'amewig': 9,\n    'amewoo': 10, 'amtspa': 11, 'annhum': 12, 'astfly': 13, 'baisan': 14,\n    'baleag': 15, 'balori': 16, 'banswa': 17, 'barswa': 18, 'bawwar': 19,\n    'belkin1': 20, 'belspa2': 21, 'bewwre': 22, 'bkbcuc': 23, 'bkbmag1': 24,\n    'bkbwar': 25, 'bkcchi': 26, 'bkchum': 27, 'bkhgro': 28, 'bkpwar': 29,\n    'bktspa': 30, 'blkpho': 31, 'blugrb1': 32, 'blujay': 33, 'bnhcow': 34,\n    'boboli': 35, 'bongul': 36, 'brdowl': 37, 'brebla': 38, 'brespa': 39,\n    'brncre': 40, 'brnthr': 41, 'brthum': 42, 'brwhaw': 43, 'btbwar': 44,\n    'btnwar': 45, 'btywar': 46, 'buffle': 47, 'buggna': 48, 'buhvir': 49,\n    'bulori': 50, 'bushti': 51, 'buwtea': 52, 'buwwar': 53, 'cacwre': 54,\n    'calgul': 55, 'calqua': 56, 'camwar': 57, 'cangoo': 58, 'canwar': 59,\n    'canwre': 60, 'carwre': 61, 'casfin': 62, 'caster1': 63, 'casvir': 64,\n    'cedwax': 65, 'chispa': 66, 'chiswi': 67, 'chswar': 68, 'chukar': 69,\n    'clanut': 70, 'cliswa': 71, 'comgol': 72, 'comgra': 73, 'comloo': 74,\n    'commer': 75, 'comnig': 76, 'comrav': 77, 'comred': 78, 'comter': 79,\n    'comyel': 80, 'coohaw': 81, 'coshum': 82, 'cowscj1': 83, 'daejun': 84,\n    'doccor': 85, 'dowwoo': 86, 'dusfly': 87, 'eargre': 88, 'easblu': 89,\n    'easkin': 90, 'easmea': 91, 'easpho': 92, 'eastow': 93, 'eawpew': 94,\n    'eucdov': 95, 'eursta': 96, 'evegro': 97, 'fiespa': 98, 'fiscro': 99,\n    'foxspa': 100, 'gadwal': 101, 'gcrfin': 102, 'gnttow': 103, 'gnwtea': 104,\n    'gockin': 105, 'gocspa': 106, 'goleag': 107, 'grbher3': 108, 'grcfly': 109,\n    'greegr': 110, 'greroa': 111, 'greyel': 112, 'grhowl': 113, 'grnher': 114,\n    'grtgra': 115, 'grycat': 116, 'gryfly': 117, 'haiwoo': 118, 'hamfly': 119,\n    'hergul': 120, 'herthr': 121, 'hoomer': 122, 'hoowar': 123, 'horgre': 124,\n    'horlar': 125, 'houfin': 126, 'houspa': 127, 'houwre': 128, 'indbun': 129,\n    'juntit1': 130, 'killde': 131, 'labwoo': 132, 'larspa': 133, 'lazbun': 134,\n    'leabit': 135, 'leafly': 136, 'leasan': 137, 'lecthr': 138, 'lesgol': 139,\n    'lesnig': 140, 'lesyel': 141, 'lewwoo': 142, 'linspa': 143, 'lobcur': 144,\n    'lobdow': 145, 'logshr': 146, 'lotduc': 147, 'louwat': 148, 'macwar': 149,\n    'magwar': 150, 'mallar3': 151, 'marwre': 152, 'merlin': 153, 'moublu': 154,\n    'mouchi': 155, 'moudov': 156, 'norcar': 157, 'norfli': 158, 'norhar2': 159,\n    'normoc': 160, 'norpar': 161, 'norpin': 162, 'norsho': 163, 'norwat': 164,\n    'nrwswa': 165, 'nutwoo': 166, 'olsfly': 167, 'orcwar': 168, 'osprey': 169,\n    'ovenbi1': 170, 'palwar': 171, 'pasfly': 172, 'pecsan': 173, 'perfal': 174,\n    'phaino': 175, 'pibgre': 176, 'pilwoo': 177, 'pingro': 178, 'pinjay': 179,\n    'pinsis': 180, 'pinwar': 181, 'plsvir': 182, 'prawar': 183, 'purfin': 184,\n    'pygnut': 185, 'rebmer': 186, 'rebnut': 187, 'rebsap': 188, 'rebwoo': 189,\n    'redcro': 190, 'redhea': 191, 'reevir1': 192, 'renpha': 193, 'reshaw': 194,\n    'rethaw': 195, 'rewbla': 196, 'ribgul': 197, 'rinduc': 198, 'robgro': 199,\n    'rocpig': 200, 'rocwre': 201, 'rthhum': 202, 'ruckin': 203, 'rudduc': 204,\n    'rufgro': 205, 'rufhum': 206, 'rusbla': 207, 'sagspa1': 208, 'sagthr': 209,\n    'savspa': 210, 'saypho': 211, 'scatan': 212, 'scoori': 213, 'semplo': 214,\n    'semsan': 215, 'sheowl': 216, 'shshaw': 217, 'snobun': 218, 'snogoo': 219,\n    'solsan': 220, 'sonspa': 221, 'sora': 222, 'sposan': 223, 'spotow': 224,\n    'stejay': 225, 'swahaw': 226, 'swaspa': 227, 'swathr': 228, 'treswa': 229,\n    'truswa': 230, 'tuftit': 231, 'tunswa': 232, 'veery': 233, 'vesspa': 234,\n    'vigswa': 235, 'warvir': 236, 'wesblu': 237, 'wesgre': 238, 'weskin': 239,\n    'wesmea': 240, 'wessan': 241, 'westan': 242, 'wewpew': 243, 'whbnut': 244,\n    'whcspa': 245, 'whfibi': 246, 'whtspa': 247, 'whtswi': 248, 'wilfly': 249,\n    'wilsni1': 250, 'wiltur': 251, 'winwre3': 252, 'wlswar': 253, 'wooduc': 254,\n    'wooscj2': 255, 'woothr': 256, 'y00475': 257, 'yebfly': 258, 'yebsap': 259,\n    'yehbla': 260, 'yelwar': 261, 'yerwar': 262, 'yetvir': 263\n}\n\nINV_BIRD_CODE = {v: k for k, v in BIRD_CODE.items()}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"df          = pd.read_csv(config.train_csv)[['ebird_code', 'filename', 'duration']]\ndf['path']  = config.train_dir + df['ebird_code'] + '/' + df['filename']\ndf['label'] = df['ebird_code'].map(BIRD_CODE)\nprint(df.columns)\ndf[['ebird_code', 'label']].sample(5)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class RandomStretchMelSpectrogram(nn.Module):\n    def __init__(self, sample_rate, n_fft, top_db):\n        super().__init__()\n        self.time_stretch = TimeStretch(hop_length=None, n_freq=n_fft//2+1)\n        self.stft = Spectrogram(n_fft=n_fft, power=None)\n        self.com_norm = ComplexNorm(power=2.)\n        self.mel_specgram = MelSpectrogram(sample_rate, n_fft=n_fft, f_max=8000)\n        self.AtoDB = AmplitudeToDB(top_db=top_db)\n    \n    def forward(self, x):\n        x = self.stft(x)\n        x = self.com_norm(x)\n        x = self.mel_specgram.mel_scale(x)\n        x = self.AtoDB(x)\n\n        return x","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class cnn_birdcall_v1(nn.Module):\n    def __init__(self, sample_rate=32000, output_class=264, d_size=256, n_fft=2**11, top_db=80):\n        super().__init__()\n        \n        self.mel = RandomStretchMelSpectrogram(sample_rate, n_fft, top_db)#, max_perc)\n        \n        self.conv1 = nn.Conv2d(1, 64, kernel_size=3, stride=(1, 1))\n        self.bn1 = nn.BatchNorm2d(64)\n        self.relu = nn.ReLU(0.1)\n        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2)\n        #self.dropout = nn.Dropout(0.1)\n\n        self.conv2 = nn.Conv2d(64, 128, kernel_size=3, stride=(1, 1))\n        self.bn2 = nn.BatchNorm2d(128)\n        self.relu2 = nn.ReLU(0.1)\n        self.maxpool2 = nn.MaxPool2d(kernel_size=3, stride=2)\n        #self.dropout2 = nn.Dropout(0.1)\n        \n        self.conv3 = nn.Conv2d(128, 256, kernel_size=3, stride=(1, 1))\n        self.bn3 = nn.BatchNorm2d(256)\n        self.relu3 = nn.ReLU(0.1)\n        self.maxpool3 = nn.MaxPool2d(kernel_size=3, stride=3)\n        #self.dropout3 = nn.Dropout(0.1)\n        \n        self.conv4 = nn.Conv2d(256, 512, kernel_size=3, stride=(1, 1))\n        self.bn4 = nn.BatchNorm2d(512)\n        self.relu4 = nn.ReLU(0.1)\n        self.maxpool4 = nn.MaxPool2d(kernel_size=3, stride=3)\n        #self.dropout4 = nn.Dropout(0.1)\n        \n        self.lstm = nn.LSTM(6, 512, 2, batch_first=True)\n        self.dropout_lstm = nn.Dropout(0.3)\n        self.bn_lstm = nn.BatchNorm1d(512)\n        \n        self.output = nn.Linear(512, output_class)\n    \n    def forward(self, x):\n        x = self.mel(x)\n        \n        x = self.conv1(x)\n        x = self.bn1(x)\n        x = self.relu(x)\n        x = self.maxpool(x)\n        x = self.dropout(x)\n        \n        x = self.conv2(x)\n        x = self.bn2(x)\n        x = self.relu2(x)\n        x = self.maxpool2(x)\n        x = self.dropout2(x)\n        \n        x = self.conv3(x)\n        x = self.bn3(x)\n        x = self.relu3(x)\n        x = self.maxpool3(x)\n        x = self.dropout3(x)\n        \n        x = self.conv4(x)\n        x = self.bn4(x)\n        x = self.relu4(x)\n        x = self.maxpool4(x)\n        x = self.dropout4(x)\n        \n        x, _ = self.lstm(x.view(x.size(0), 512, 6), None)\n        x = self.dropout_lstm(x[:, -1, :])\n        x = self.bn_lstm(x)\n        \n        x = x.view(-1, 512)\n        x = self.output(x) # adicionar softmax\n        \n        return x     \n        \n        ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class TrainDataset(data.Dataset):\n    \"\"\"Bird Sound dataset.\"\"\"\n\n    def __init__(self, df):\n        \"\"\"\n        Args:\n            df (pd.DataFrame): must have ['path', 'label'] columns\n        \"\"\"\n        self.df = df\n\n    def __len__(self):\n        return len(self.df)\n    \n    \n    def loadMP3(self, path, duration):\n        \"\"\"\n        returns MFCC of a given waveform\n        \"\"\"\n        #print(path)\n        try:\n            audio, sample_rate = torchaudio.load(path)\n            mfcc = MelSpectrogram(config.mel_num, hop_length=512)\n            mels = mfcc(audio)\n            return mels\n            # arrumamos -> o path estava chegando errado e tinhamos esquecido de trocar o MFCC do librosa\n            # mels will be of shape (N_MELS, ceil(duration*SR/512)) \n            # 512 here is default hop length\n\n        except Exception as e:\n            print('b')\n            print(\"Error encountered while parsing file: \", path, e)\n            mels = np.zeros((config.mel_num, config.max_len*config.target_sample_rate//512), dtype=np.float32)\n            return mels\n            \n\n    def __getitem__(self, idx):\n        if torch.is_tensor(idx):\n            idx = idx.tolist()\n        \n        path = self.df['path'].iloc[idx]\n        duration = self.df['duration'].iloc[idx]\n        #if duration < config.max_len:\n        #    duration = None # read entire file\n        #else:\n        #    duration = config.max_len\n        #if os.path.exists(\"./\"+path.split('/')[-1]+\".npy\"):\n        #    mels = np.load(\"./\"+path.split('/')[-1]+\".npy\")\n        #else:\n        mels = self.loadMP3(path, duration)\n        np.save(\"./\"+path.split('/')[-1]+\".npy\", mels)\n        label  = self.df['label'].iloc[idx]\n        sample = {'label':label, 'features': mels, 'duration': duration}\n        return sample","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class TestDataset(data.Dataset):\n    def __init__(self, df:pd.DataFrame, clip):\n        self.df = df\n        self.clip = clip\n        \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, idx:int):\n        sample_rate = 32000\n        sample = self.df.loc[idx, :]\n        site = sample.site\n        row_id = sample.row_id\n        \n        if site == \"site_3\":\n            print(self.clip.size())\n            # shape [amplitude, sequence_length]\n            len_y = self.clip.size()[1]\n            start = 0\n            end = sample_rate * 5\n            waveforms = []\n            while len_y > start:\n                waveform = self.clip[:, start:end]\n                if waveform.size(1) != (sample_rate * 5):\n                    break\n                start = end\n                end = end + sample_rate * 5\n                waveforms.append(waveform.numpy())\n                \n            waveforms = torch.tensor(waveforms)\n            return waveforms, row_id, site\n        \n        else:\n            end_seconds = int(sample.seconds)\n            start_seconds = int(end_seconds - 5)\n            \n            start_index = sample_rate * start_seconds\n            end_index   = sample_rate * end_seconds\n            \n            waveform = self.clip[:, start_index:end_index]\n\n            return waveform, row_id, site","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ds = TrainDataset(df)\nds[0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Train Val split\ndf = df.sample(frac=1).reset_index(drop=True)\ntrain_len = int(len(df) * (1-config.frac))\ntrain_df = df.iloc[:train_len]\nvalid_df = df.iloc[train_len:]\ntrain_df.shape, valid_df.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def collate_fn_wrap(batch):\n    '''\n    wraps batch of variable length\n    '''\n    \n    ## get sequence lengths\n    lengths = [t['features'].shape[1] for t in batch]\n    maxlen = max(312, random.choice(lengths))#max(lengths)\n    \n    for i in range(len(batch)):\n        batch[i]['features'] = torch.from_numpy(batch[i]['features'])\n        k = math.ceil(maxlen/lengths[i])\n        batch[i]['features'] = batch[i]['features'].repeat(1, k)[:, :maxlen]\n        # assert batch[i]['features'].shape[1] == maxlen\n        \n    labels = torch.tensor([i['label'] for i in batch])\n    features = torch.stack([i['features'] for i in batch])\n    return {'features':features, 'labels':labels}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# prepare data loaders\ntrain_loader = torch.utils.data.DataLoader(TrainDataset(train_df),\n                                           batch_size=config.batch_size, \n                                           num_workers=4, \n                                           shuffle=True, \n                                           collate_fn=collate_fn_wrap,\n                                           drop_last = True)\n\nvalid_loader = torch.utils.data.DataLoader(TrainDataset(valid_df), \n                                           batch_size=config.batch_size, \n                                           num_workers=4, \n                                           shuffle=False, \n                                           collate_fn=collate_fn_wrap,\n                                           drop_last = True)\n\nprint(len(train_loader), len(valid_loader))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = cnn_birdcall_v1()\nmodel.to(config.device)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"criterion = torch.nn.CrossEntropyLoss()\noptimizer = torch.optim.Adam(model.parameters(), lr = 10e-4)\nscheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=4, gamma=0.5)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# number of epochs to train the model\nn_epochs = 20\n\nvalid_loss_min = np.Inf # track change in validation loss\n\nfor epoch in range(1, n_epochs+1):\n\n    # keep track of training and validation loss\n    train_loss = 0.0\n    valid_loss = 0.0\n    \n    ###################\n    # train the model #\n    ###################\n    model.train()\n    \n    bar = tqdm(train_loader, total=len(train_loader), leave=False)\n    for data in bar:\n        print(type(data))\n        \n        \n        features = data['features'].to(device)\n        target = data['labels'].to(device)\n        \n        optimizer.zero_grad()\n        \n        #inputs, targets_a, targets_b, lam = mixup_data(features, target, 0.2, use_cuda=torch.cuda.is_available())\n        outputs = model(inputs)\n        #loss_func = mixup_criterion(targets_a, targets_b, lam)\n        loss = criterion(outputs, target)\n        #loss = loss_func(criterion, outputs)\n        \n        loss.backward()\n        \n        optimizer.step()\n        \n        bar.set_postfix({'loss': loss.item()})\n        train_loss += loss.item()*features.size(0)\n        \n    ######################    \n    # validate the model #\n    ######################\n    with torch.no_grad():\n        targets = []\n        preds = []\n        model.eval()\n        bar = tqdm(valid_loader, total=len(valid_loader), leave=False)\n        for data in bar:\n            features = data['features'].to(device)\n            target = data['labels'].to(device)\n            \n            output = model(features)\n            loss = criterion(output, target)\n            \n            pred = torch.argmax(output, dim=1)\n            \n            targets.extend(target.cpu().detach().numpy().tolist())\n            preds.extend(pred.cpu().detach().numpy().tolist())\n            \n            # update average validation loss\n            valid_loss += loss.item()*features.size(0)\n    \n    acc = np.sum(np.array(preds) == np.array(targets)) / len(preds)\n    \n    \n    scheduler.step()\n    \n    # calculate average losses\n    train_loss = train_loss/len(train_loader.dataset)\n    valid_loss = valid_loss/len(valid_loader.dataset)\n        \n    # print training/validation statistics \n    print('Epoch: {} \\tValidation Acc: {:.6f}'.format(epoch, acc))\n    print('Training Loss: {:.6f} \\tValidation Loss: {:.6f}'.format(train_loss, valid_loss))\n    \n    # save model if validation loss has decreased\n    if valid_loss <= valid_loss_min:\n        print('Validation loss decreased ({:.6f} --> {:.6f}).  Saving model ...'.format(\n        valid_loss_min,\n        valid_loss))\n        torch.save(model.state_dict(), 'model_cifar.pt')\n        valid_loss_min = valid_loss","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def predicter(test_df: pd.DataFrame, \n                        clip: np.ndarray, \n                        model,\n                        threshold=0.5):\n    \n    dataset = TestDataset(df=test_df, \n                          clip=clip)\n    loader = DataLoader(dataset, batch_size=1, shuffle=False)\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    \n    model = model.to(device)\n    model.eval()\n    \n    prediction_dict = {}\n    for waveform, row_id, site in progress_bar(loader):\n        site = site[0]\n        row_id = row_id[0]\n        \n        if site in {\"site_1\", \"site_2\"}:\n            waveform = waveform.to(device)\n\n            with torch.no_grad():\n                prediction = model(waveform)\n                proba = prediction.detach().cpu().numpy().reshape(-1)\n\n            event = proba > threshold\n            labels = np.argwhere(event).reshape(-1).tolist()\n                \n        else:\n            # to avoid prediction on large batch\n            waveform = waveform.squeeze(0)\n            batch_size = 16\n            whole_size = waveform.size()[0]\n            if whole_size % batch_size == 0:\n                n_iter = whole_size // batch_size\n            else:\n                n_iter = whole_size // batch_size + 1\n                \n            all_events = set()\n            for batch_i in range(n_iter):\n                batch = waveform[batch_i * batch_size : (batch_i + 1) * batch_size, :, :]\n\n                batch = batch.to(device)\n                with torch.no_grad():\n                    prediction = model(batch)\n                    proba = prediction.detach().cpu().numpy()\n                    \n                global g\n                g = proba\n                \n                events = proba >= threshold\n                for i in range(len(events)):\n                    event = events[i, :]\n                    labels = np.argwhere(event).reshape(-1).tolist()\n                    for label in labels:\n                        all_events.add(label)\n                        \n            labels = list(all_events)\n            \n        if len(labels) == 0:\n            prediction_dict[row_id] = \"nocall\"\n        else:\n            labels_str_list = list(map(lambda x: INV_BIRD_CODE[x], labels))\n            label_string = \" \".join(labels_str_list)\n            prediction_dict[row_id] = label_string\n            \n    return prediction_dict","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def prediction(test_df: pd.DataFrame,\n               test_audio: Path,\n               model,\n               threshold=0.5):\n    \n    unique_audio_id = test_df.audio_id.unique()\n\n    prediction_dfs = []\n    for audio_id in unique_audio_id:\n        clip, _ = torchaudio.load(test_audio / (audio_id + \".mp3\"), normalization=True)\n        test_df_for_audio_id = test_df.query(\n            f\"audio_id == '{audio_id}'\").reset_index(drop=True)\n        with timer(f\"Prediction on {audio_id}\", logger):\n            prediction_dict = predicter(test_df_for_audio_id,\n                                                  clip=clip[0].unsqueeze(0),\n                                                  model=model,\n                                                  threshold=threshold)\n            \n        row_id = list(prediction_dict.keys())\n        birds = list(prediction_dict.values())\n        prediction_df = pd.DataFrame({\n            \"row_id\": row_id,\n            \"birds\": birds\n        })\n        prediction_dfs.append(prediction_df)\n    \n    prediction_df = pd.concat(prediction_dfs, axis=0, sort=False).reset_index(drop=True)\n    return prediction_df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = cnn_birdcall_v1()\ncheckpoint = torch.load(\"\") # TODO: train and save a model\nmodel.load_state_dict(checkpoint['model_state_dict'])\n\nsubmission = prediction(test_df=test,\n                        test_audio=test_audio,\n                        model=model,\n                        threshold=0.0)\nsubmission.to_csv(\"submission.csv\", index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"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":4,"nbformat_minor":4}