{"cells":[{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"!pip install pretrainedmodels\n!pip install pydub","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport librosa\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom tqdm import tqdm\nimport random\n\nfrom sklearn.preprocessing import LabelEncoder\nfrom sklearn.model_selection import KFold, StratifiedKFold\nimport math\nfrom collections import OrderedDict\n\nfrom PIL import Image\nimport albumentations\nfrom pydub import AudioSegment\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\nfrom torch.utils.data import Dataset, DataLoader\n\nimport pretrainedmodels\n\nimport warnings\nwarnings.filterwarnings('ignore')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Preprocessing <a id=\"3\"></a>","execution_count":null},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"train = pd.read_csv(\"../input/birdsong-recognition/train.csv\")\ntest = pd.read_csv(\"../input/birdsong-recognition/test.csv\")\nsubmission = pd.read_csv(\"../input/birdsong-recognition/sample_submission.csv\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### e-bird code\n\na code for the bird species. we need to predict `ebird_code` using metadata and audio data \n","execution_count":null},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"print(\"Number of Unique birds : \", train.ebird_code.nunique())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### top10 Birds\n\nwe are taking top10 birds to build stater model","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"top10_birds = list(train.ebird_code.value_counts().index[:10])\n\ntrain = train[train.ebird_code.isin(top10_birds)]\n\n# label encoding for target values\ntrain[\"ebird_label\"] = LabelEncoder().fit_transform(train.ebird_code.values)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### K-Fold","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train.loc[:, \"kfold\"] = -1\n\ntrain= train.sample(frac=1).reset_index(drop=True)\n\nX = train.filename.values\ny = train.ebird_code.values\n\nkfold = StratifiedKFold(n_splits=5)\n\nfor fold, (t_idx, v_idx) in enumerate(kfold.split(X, y)):\n    train.loc[v_idx, \"kfold\"] = fold\n\nprint(train.kfold.value_counts())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Arguments","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"class args:\n    \n    ROOT_PATH = \"../input/birdsong-recognition/train_audio\"\n    \n    num_classes = 10\n    max_duration= 5 # seconds\n    \n    sample_rate = 32000\n    \n    batch_size = 16\n    num_workers = 4\n    epochs = 10\n    \n    lr = 0.0009\n    wd = 1e-5\n    momentum = 0.9\n    eps = 1e-8\n    betas = (0.9, 0.999)\n    \n    melspectrogram_parameters = {\n        \"n_mels\": 128,\n        \"fmin\": 20,\n        \"fmax\": 16000\n    }\n    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Loading Audio Files","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def load_audio(path):\n    try:\n        sound = AudioSegment.from_mp3(path)\n        sound = sound.set_frame_rate(args.sample_rate)\n        sound_array = np.array(sound.get_array_of_samples(), dtype=np.float32)\n    except:\n        sound_array = np.zeros(args.sample_rate * args.max_duration, dtype=np.float32)\n        \n    return sound_array, args.sample_rate","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Audo Albumentations\n\n- check my other notebook [Audio Albumentations](https://www.kaggle.com/gopidurgaprasad/audio-albumentations)","execution_count":null},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"from albumentations.core.transforms_interface import DualTransform, BasicTransform\n\nclass AudioTransform(BasicTransform):\n    \"\"\"Transform for Audio task\"\"\"\n\n    @property\n    def targets(self):\n        return {\"data\": self.apply}\n    \n    def update_params(self, params, **kwargs):\n        if hasattr(self, \"interpolation\"):\n            params[\"interpolation\"] = self.interpolation\n        if hasattr(self, \"fill_value\"):\n            params[\"fill_value\"] = self.fill_value\n        return params\n\nclass NoiseInjection(AudioTransform):\n    \"\"\"It simply add some random value into data by using numpy\"\"\"\n    def __init__(self, noise_levels=(0, 0.5), always_apply=False, p=0.5):\n        super(NoiseInjection, self).__init__(always_apply, p)\n\n        self.noise_levels = noise_levels\n    \n    def apply(self, data, **params):\n        sound, sr = data\n        noise_level = np.random.uniform(*self.noise_levels)\n        noise = np.random.randn(len(sound))\n        augmented_sound = sound + noise_level * noise\n        # Cast back to same data type\n        augmented_sound = augmented_sound.astype(type(sound[0]))\n\n        return augmented_sound, sr\n\nclass ShiftingTime(AudioTransform):\n    \"\"\"Shifting time axis\"\"\"\n    def __init__(self, always_apply=False, p=0.5):\n        super(ShiftingTime, self).__init__(always_apply, p)\n    \n    def apply(self, data, **params):\n        sound, sr = data\n\n        shift_max = np.random.randint(1,len(sound))\n        shift = np.random.randint(int(sr * shift_max))\n        direction = np.random.randint(0,2)\n        if direction == 1:\n            shift = -shift\n\n        augmented_sound = np.roll(sound, shift)\n        # Set to silence for heading/ tailing\n        if shift > 0:\n            augmented_sound[:shift] = 0\n        else:\n            augmented_sound[shift:] = 0\n\n        return augmented_sound, sr\n\nclass PitchShift(AudioTransform):\n    \n    def __init__(self, always_apply=False, p=0.5):\n        super(PitchShift, self).__init__(always_apply, p)\n    \n    def apply(self, data, **params):\n        sound, sr = data\n\n        n_steps = np.random.randint(-10, 10)\n        augmented_sound = librosa.effects.pitch_shift(sound, sr, n_steps)\n\n        return augmented_sound, sr\n\nclass TimeStretch(AudioTransform):\n    \n    def __init__(self, always_apply=False, p=0.5):\n        super(TimeStretch, self).__init__(always_apply, p)\n    \n    def apply(self, data, **params):\n        sound, sr = data\n\n        rate = np.random.uniform(0, 2)\n        augmented_sound = librosa.effects.time_stretch(sound, rate)\n\n        return augmented_sound, sr\n\nclass RandomAudio(AudioTransform):\n    \n    def __init__(self,  seconds=5, always_apply=False, p=0.5):\n        super(RandomAudio, self).__init__(always_apply, p)\n\n        self.seconds = seconds\n    \n    def apply(self, data, **params):\n        sound, sr = data\n\n        shift = np.random.randint(len(sound))\n        trim_sound = np.roll(sound, shift)\n\n        min_samples = int(sr * self.seconds)\n\n        if len(trim_sound) < min_samples:\n            padding = min_samples - len(trim_sound)\n            offset = padding // 2\n            trim_sound = np.pad(trim_sound, (offset, padding - offset), \"constant\")\n        else:\n            trim_sound = trim_sound[:min_samples]\n\n        return trim_sound, sr\n\nclass MelSpectrogram(AudioTransform):\n\n    def __init__(self, parameters, always_apply=False, p=0.5):\n        super(MelSpectrogram, self).__init__(always_apply, p)\n\n        self.parameters = parameters\n    \n    def apply(self, data, **params):\n        sound, sr = data\n\n        melspec = librosa.feature.melspectrogram(sound, sr=sr, **self.parameters)\n        melspec = librosa.power_to_db(melspec)\n        melspec = melspec.astype(np.float32)\n\n        return melspec, sr\n\nclass SpecAugment(AudioTransform):\n    \n    def __init__(self, num_mask=2, freq_masking=0.15, time_masking=0.20, always_apply=False, p=0.5):\n        super(SpecAugment, self).__init__(always_apply, p)\n\n        self.num_mask = num_mask\n        self.freq_masking = freq_masking\n        self.time_masking = time_masking\n    \n    def apply(self, data, **params):\n        melspec, sr = data\n\n        spec_aug = self.spec_augment(melspec, \n                                     self.num_mask,\n                                     self.freq_masking,\n                                     self.time_masking,\n                                     melspec.min())\n        \n\n\n        return spec_aug, sr\n    \n    # Source: https://www.kaggle.com/davids1992/specaugment-quick-implementation\n    def spec_augment(self, \n                    spec: np.ndarray,\n                    num_mask=2,\n                    freq_masking=0.15,\n                    time_masking=0.20,\n                    value=0):\n        spec = spec.copy()\n        num_mask = random.randint(1, num_mask)\n        for i in range(num_mask):\n            all_freqs_num, all_frames_num  = spec.shape\n            freq_percentage = random.uniform(0.0, freq_masking)\n\n            num_freqs_to_mask = int(freq_percentage * all_freqs_num)\n            f0 = np.random.uniform(low=0.0, high=all_freqs_num - num_freqs_to_mask)\n            f0 = int(f0)\n            spec[f0:f0 + num_freqs_to_mask, :] = value\n\n            time_percentage = random.uniform(0.0, time_masking)\n\n            num_frames_to_mask = int(time_percentage * all_frames_num)\n            t0 = np.random.uniform(low=0.0, high=all_frames_num - num_frames_to_mask)\n            t0 = int(t0)\n            spec[:, t0:t0 + num_frames_to_mask] = value\n\n        return spec\n\nclass SpectToImage(AudioTransform):\n\n    def __init__(self, always_apply=False, p=0.5):\n        super(SpectToImage, self).__init__(always_apply, p)\n    \n    def apply(self, data, **params):\n        image, sr = data\n        delta = librosa.feature.delta(image)\n        accelerate = librosa.feature.delta(image, order=2)\n        image = np.stack([image, delta, accelerate], axis=0)\n        image = image.astype(np.float32) / 100.0\n\n        return image","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"### Example\n\ntrain_audio_augmentation = albumentations.Compose([\n     RandomAudio(seconds=args.max_duration, always_apply=True),\n     NoiseInjection(p=0.33),\n     MelSpectrogram(parameters=args.melspectrogram_parameters,always_apply=True),\n     SpecAugment(p=0.33),\n     SpectToImage(always_apply=True)\n])\n\nvalid_audio_augmentation = albumentations.Compose([\n     RandomAudio(seconds=args.max_duration, always_apply=True),\n     MelSpectrogram(parameters=args.melspectrogram_parameters,always_apply=True),\n     SpectToImage(always_apply=True)\n])\n\npath = f\"{args.ROOT_PATH}/aldfly/XC134874.mp3\"\ndata = load_audio(path)\nimage = train_audio_augmentation(data=data)['data']\n\nplt.imshow(image.transpose(1,2,0))\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Pytorch DataLoader","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"class BirdDataset:\n    def __init__(self, df, valid=False):\n        \n        self.filename = df.filename.values\n        self.ebird_label = df.ebird_label.values\n        self.ebird_code = df.ebird_code.values\n        \n        if valid:\n            self.aug = valid_audio_augmentation\n        else:\n            self.aug = train_audio_augmentation\n        \n    \n    def __len__(self):\n        return len(self.filename)\n    \n    def __getitem__(self, item):\n        \n        filename = self.filename[item]\n        ebird_code = self.ebird_code[item]\n        ebird_label = self.ebird_label[item]\n\n        data = load_audio(f\"{args.ROOT_PATH}/{ebird_code}/{filename}\")\n        spect = self.aug(data=data)[\"data\"]\n        \n        target = ebird_label\n        \n        return {\n            \"spect\" : torch.tensor(spect, dtype=torch.float), \n            \"target\" : torch.tensor(target, dtype=torch.long)\n        }\n    ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Example \ndataset = BirdDataset(train)\nd = dataset.__getitem__(10)\n\nprint(d[\"spect\"].shape, d[\"target\"])\n\nplt.imshow(d[\"spect\"].permute(1,2,0))\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### ResNet18 Model","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"class ResNet18(nn.Module):\n    def __init__(self, pretrained):\n        super(ResNet18, self).__init__()\n        if pretrained is True:\n            self.model = pretrainedmodels.__dict__[\"resnet18\"](pretrained=\"imagenet\")\n        else:\n            self.model = pretrainedmodels.__dict__[\"resnet18\"](pretrained=None)\n        \n        self.l0 = nn.Linear(512, args.num_classes)\n        \n    def forward(self, x):\n        bs, _, _, _ = x.shape\n        x = self.model.features(x)\n        x = F.adaptive_avg_pool2d(x, 1).reshape(bs, -1)\n        x = self.l0(x)\n        \n        return x\n    ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Utility functions","execution_count":null},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"def to_list(tensor):\n    return tensor.detach().cpu().tolist()\n\nclass AverageMeter(object):\n    \"\"\"Computes and stores the average and current values\"\"\"\n    def __init__(self):\n        self.reset()\n    \n    def __init__(self):\n        self.val = 0\n        self.avg = 0\n        self.sum = 0\n        self.count = 0\n\n    def update(self, val, n=1):\n        self.val = val\n        self.sum += val * n\n        self.count += n\n        self.avg = self.sum / self.count\n\ndef get_position_accuracy(logits, labels):\n    predictions = np.argmax(F.softmax(logits, dim=1).cpu().data.numpy(), axis=1)\n    labels = labels.cpu().data.numpy()\n    total_num = 0\n    sum_correct = 0\n    for i in range(len(labels)):\n        if labels[i] >= 0:\n            total_num += 1\n            if predictions[i] == labels[i]:\n                sum_correct += 1\n    if total_num == 0:\n        total_num = 1e-7\n    return np.float32(sum_correct) / total_num, total_num","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Loss function","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def loss_fn(preds, labels):\n    loss = nn.CrossEntropyLoss(ignore_index=-1)(preds, labels)\n    return loss","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### train & validation functions","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def train_fn(train_loader, model, optimizer, epoch):\n    total_loss = AverageMeter()\n    accuracies = AverageMeter()\n    \n    model.train()\n\n    t = tqdm(train_loader)\n    for step, d in enumerate(t):\n        \n        spect = d[\"spect\"].to(args.device)\n        targets = d[\"target\"].to(args.device)\n        \n        outputs = model(spect)\n\n        loss = loss_fn(outputs, targets)\n\n        acc, n_position = get_position_accuracy(outputs, targets)\n        \n\n        total_loss.update(loss.item(), n_position)\n        accuracies.update(acc, n_position)\n\n        optimizer.zero_grad()\n        \n        loss.backward()\n        optimizer.step()\n        \n        t.set_description(f\"Train E:{epoch+1} - Loss:{total_loss.avg:0.4f} - Acc:{accuracies.avg:0.4f}\")\n    \n    return total_loss.avg\n\ndef valid_fn(valid_loader, model, epoch):\n    total_loss = AverageMeter()\n    accuracies = AverageMeter()\n    \n    model.eval()\n\n    t = tqdm(valid_loader)\n    for step, d in enumerate(t):\n        \n        with torch.no_grad():\n        \n            spect = d[\"spect\"].to(args.device)\n            targets = d[\"target\"].to(args.device)\n\n            outputs = model(spect)\n\n            loss = loss_fn(outputs, targets)\n\n            acc, n_position = get_position_accuracy(outputs, targets)\n\n\n            total_loss.update(loss.item(), n_position)\n            accuracies.update(acc, n_position)\n            \n            t.set_description(f\"Eval E:{epoch+1} - Loss:{total_loss.avg:0.4f} - Acc:{accuracies.avg:0.4f}\")\n\n    return total_loss.avg, accuracies.avg","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def main(fold_index):\n    \n    model = ResNet18(pretrained=False)\n    \n    args.device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    \n    \n    # Setting seed\n    seed = 42\n    random.seed(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed(seed)\n    torch.backends.cudnn.deterministic = True\n    \n    model.to(args.device)\n    \n    optimizer = torch.optim.AdamW(model.parameters(),\n                                      lr=args.lr,\n                                      betas=args.betas,\n                                      eps=args.eps,\n                                      weight_decay=args.wd)\n    \n    train_df = train[~train.kfold.isin([fold_index])]\n    \n    train_dataset = BirdDataset(df=train_df)\n    \n    train_loader = DataLoader(\n        dataset = train_dataset,\n        batch_size = args.batch_size,\n        shuffle = True,\n        num_workers = args.num_workers,\n        pin_memory = True,\n        drop_last = False\n    )\n    \n    \n    valid_df = train[train.kfold.isin([fold_index])]\n    \n    valid_dataset = BirdDataset(df=valid_df, valid=True)\n    \n    valid_loader = DataLoader(\n        dataset = valid_dataset,\n        batch_size = args.batch_size,\n        shuffle = False,\n        num_workers = args.num_workers,\n        pin_memory = True,\n        drop_last = False\n    )\n    \n    best_acc = 0\n    \n    for epoch in range(args.epochs):\n        train_loss = train_fn(train_loader, model, optimizer, epoch)\n        valid_loss, valid_acc = valid_fn(valid_loader, model, epoch)\n        \n        print(f\"**** Epoch {epoch+1} **==>** Accuracy = {valid_acc}\")\n        \n        if valid_acc > best_acc:\n            print(\"**** Model Improved !!!! Saving Model\")\n            torch.save(model.state_dict(), f\"fold_{fold_index}.bin\")\n            best_acc = valid_acc  ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### 5 Folds","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"# fold0\nmain(0)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<h2 style=\"color:red;\"> Please upvote if you like it. It motivates me. Thank you ☺️ .</h2>","execution_count":null}],"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}