{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"markdown","source":"# Birdcall Submission Module","execution_count":null},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import os\nimport cv2\nimport torch\nimport torch.nn as nn\nimport torchvision.models as models\nimport torch.nn.functional as F\nimport torch.utils.data as data\nimport numpy as np\nimport pandas as pd\nfrom pathlib import Path\nimport torchaudio\nfrom collections import OrderedDict","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Define Device","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Architecture of Pre-Trained Model\n- Squishing Melspectrogram into 224x224x3 and expanding the intensity dimension.\n- Applying pre-trained computer vision models like VGG, InceptionNet.\n- Output using sigmoidal activation function: 264 classes.\n- If none of the predictions for the bird vector are over a certain threshold, then it is classified as nocall.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"class FCN(nn.Module):\n    def __init__(self, num_classes=264, pretrained=False):\n        super().__init__()\n        self.head = models.vgg16_bn(pretrained=pretrained)\n        self.head.classifier[6] = nn.Sequential(\n            nn.Linear(4096, 1024, bias=True),\n            nn.BatchNorm1d(1024),\n            nn.Dropout(.25),\n            nn.ReLU(),\n            nn.Linear(1024, 512, bias=True),\n            nn.BatchNorm1d(512),\n            nn.Dropout(.5),\n            nn.ReLU(),\n            nn.Linear(512, 264, bias=True),\n        )\n    \n    def freeze(self, n_top=0, freeze_head=True):\n        \"\"\"\n        :param n_top: Number of layers to freeze off the top classification layer.\n        :param freeze_head: If the feature extractor of the network should be frozen.\n        \"\"\"\n        self.head.features.requires_grad = not freeze_head\n        for head in range(n_top):\n            self.head.classifier[head].requires_grad = False\n    \n    def forward(self, x):\n        x = x.repeat(1, 3, 1, 1)\n        x = self.head(x)\n        return torch.sigmoid(x)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = FCN()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Detect Hidden Directory and Load Submission Files\n- There is an *invisible* test set, so we need to detect whether or not it exists during submission.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"os.listdir('/kaggle/input/birdsong-recognition/')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"TEST = Path('/kaggle/input/birdsong-recognition/test_audio').exists()\nROOT = Path('/kaggle/input/birdsong-recognition/')\nif TEST:\n    TEST_DIR = Path('/kaggle/input/birdsong-recognition/test_audio')\n    META_DIR = Path('/kaggle/input/birdsong-recognition/')\nelse:\n    TEST_DIR = Path('/kaggle/input/birdcall-check/test_audio')\n    META_DIR = Path('/kaggle/input/birdcall-check/')\nmeta = pd.read_csv(META_DIR / 'test.csv')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Test Dataset Class\n- Helper functions to create MelSpectrogram: mono-to-color spectrogram conversion.\n- Dataset object that acts as an interface for the test data.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"class TestDataset(data.Dataset):\n    def __init__(self, meta: pd.DataFrame, test_dir: Path, device, img_size=224):\n        self.root = test_dir\n        self.meta = meta\n        self.device = device\n        self.img_size = img_size\n        self.amptodb = torchaudio.transforms.AmplitudeToDB()\n    \n    def __len__(self):\n        return len(self.meta)\n    \n    def __getitem__(self, idx):\n        sample = self.meta.iloc[idx]\n        y, sr = torchaudio.load(self.root / f'{sample.audio_id}.mp3')\n        y = y[0].reshape(y.shape[1])\n        y = y.to(self.device)\n        if sample.site in ['site_1', 'site_2']:\n            start, end = sr * (sample.seconds - 5), sr * (sample.seconds)\n            y = y[int(start):int(end)]\n        else:\n            start, end = sr * (0), sr * (5)\n            y = y[int(start):int(end)]\n        spectrogram = torchaudio.transforms.MelSpectrogram(sr, n_fft=2048, hop_length=512, f_min=10, f_max=1600).to(self.device)\n        y = spectrogram(y).detach().cpu()\n        y = self.amptodb(y)\n        y = y.reshape([1, y.shape[1], y.shape[0]])\n        \n        return sample.row_id, y","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Inference\n- Define the labels map, where each index matches to a specific bird.\n- Loading pre-trained model.\n- Creating test set object.\n- Looping through test set object and creating predictions.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train_meta = pd.read_csv(Path(ROOT / 'train.csv'))\nebird_codes = {index: code for index, code in enumerate(train_meta.ebird_code.unique())}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"ebird_codes","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = FCN()\ncheckpoints = torch.load('/kaggle/input/pretrained-models-cornell-birdcall-recognition/model1.pth', map_location=device)\n\nnew_state_dict = OrderedDict()\nfor k, v in checkpoints.items():\n    if 'module' not in k:\n        k = 'module.'+k\n    else:\n        k = k[7:]\n    new_state_dict[k]=v\n\nmodel.load_state_dict(new_state_dict)\nmodel = model.to(device)\nmodel.eval()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"TEST_DATASET = TestDataset(meta, TEST_DIR, device=device)\nTHRESHOLD = 0.80","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission = []\nfor sample in range(len(TEST_DATASET)):\n    row_id, image = TEST_DATASET[sample]\n    image = image.reshape((1, *image.shape))\n    image = image.to(device, dtype=torch.float)\n    \n    outputs = model(image)\n    outputs = outputs.cpu().detach().numpy()\n    \n    # Create output string\n    indicies = (outputs>=THRESHOLD).nonzero()[1]\n    if len(indicies) == 0:\n        s = 'nocall'\n    else:\n        s = ' '.join([ebird_codes[i] for i in indicies])\n    submission.append([row_id, s])\nsub = pd.DataFrame(submission, columns=['row_id', 'birds'])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sub.to_csv('submission.csv', index=False)","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}