{"cells":[{"metadata":{},"cell_type":"markdown","source":"I have converted the mp3 files to wav files using the mpg123 command and and uploaded as 5 different datasets (it was too large to fit in a single dataset). Have used melspectrogram features to train a resnet18 model. You can try other features/model to get good results.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install efficientnet_pytorch","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 torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torch.utils.data as data\nimport torchvision\nimport torchvision.models as models # resnet18 pretrained model\nimport librosa # for feature extraction\nimport scipy # to load wav files\nfrom efficientnet_pytorch import EfficientNet","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import warnings\nwarnings.filterwarnings('ignore')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"input_path = '/kaggle/input/'","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"train_df = pd.read_csv('/kaggle/input/birdsong-recognition/train.csv')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Unfortunately for some reason the following file names are not present in the converted dataset (maybe some conversion error).","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df = train_df[(train_df.filename != 'XC395021.mp3') & (train_df.filename != 'XC504005.mp3') & (train_df.filename != 'XC504006.mp3') & (train_df.filename != 'XC505006.mp3')]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"I was not able to put all the wav files inside a single dataset, so I have used 5 datasets of about 30GB each. The following code will map each bird folder to one of the five datasets.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"import os\nwav_folders = [\n    'birdsong-wav-1',\n    'birdsong-wav-2',\n    'birdsong-wav-3',\n    'birdsong-wav-4',\n    'birdsong-wav-5',\n]\nbird_folder = {\n    bird: folder for folder in wav_folders for bird in os.listdir(os.path.join(input_path, folder)) \n}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"row = train_df.iloc[0]\nebird_code = row.ebird_code\nfile_name = row.filename\nfile_path = f'{input_path}/{bird_folder[ebird_code]}/{ebird_code}/{file_name.replace(\"mp3\", \"wav\")}'\nsr, audio = scipy.io.wavfile.read(file_path)\nif len(audio.shape) == 2:\n    audio = audio[:, 0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import IPython.display as ipd\nipd.Audio(file_path)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"bird_codes = sorted(list(set(train_df['ebird_code'])))\nbird_to_idx = { bird: idx for idx, bird in enumerate(bird_codes) }","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class Dataset(data.Dataset):\n\n    def __init__(self, df):\n        self.df = df\n\n    def __getitem__(self, index):\n\n        row = self.df.iloc[index]\n        ebird_code = row.ebird_code\n        file_name = row.filename\n        file_path = f'{input_path}/{bird_folder[ebird_code]}/{ebird_code}/{file_name.replace(\"mp3\", \"wav\")}'\n        sr, audio = scipy.io.wavfile.read(file_path)\n        if len(audio.shape) == 2:\n            audio = audio[:, 0]\n        i = np.random.randint(len(audio) - 480000) if len(audio) > 480000 else 0\n        audio = audio[i:i+480000].astype('float')\n        audio = np.pad(audio, (0, 480000 - len(audio)))\n        # Generate a melspectrogram with 256 mels.\n        mel = librosa.feature.melspectrogram(audio, sr=sr, n_mels=256)\n        mel = (mel - mel.mean()) / (mel.std() + 1e-12)\n        mel = mel[None, ...]\n        return mel, bird_to_idx[row['ebird_code']]\n\n    def __len__(self):\n        return len(self.df)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import sklearn.utils as utils \ntrain_df = utils.shuffle(train_df, random_state=42)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df.shape","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Keep 20000 for train, and 1371 for validation","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"train_dataset = Dataset(df=train_df.iloc[:20000].reset_index(drop=True))\nval_dataset = Dataset(df=train_df.iloc[20000:].reset_index(drop=True))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_dataloader = data.DataLoader(dataset=train_dataset, shuffle=True, batch_size=12, num_workers=2, pin_memory=True)\nval_dataloader = data.DataLoader(dataset=val_dataset, shuffle=True, batch_size=6, num_workers=2, pin_memory=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class MyModel(nn.Module):\n\n    def __init__(self):\n        super(MyModel, self).__init__()\n        # Convert 1 channel to 3 channel to be able to send to resnet18\n        self.conv1 = nn.Conv2d(1, 3, kernel_size=3, padding=1)\n        self.base_model = EfficientNet.from_pretrained('efficientnet-b2')\n        self.fc2 = nn.Linear(1000, 264) # 264 different birds\n\n    def forward(self, x):\n        \n        x = self.conv1(x)\n        x = self.base_model(x)\n        x = self.fc2(x)\n        \n        return x\n        \n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"device = 'cuda:0' if torch.cuda.is_available() else 'cpu'\nmodel = MyModel().to(device)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_accuracy(y_pred, y_actual):\n    y_pred_ = y_pred.argmax(1).detach().cpu().numpy()\n    y_actual_ = y_actual.numpy()\n    \n    return np.mean(y_pred_ == y_actual_) * 100.0","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"criterion = nn.CrossEntropyLoss()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"opt = torch.optim.Adam(model.parameters(), lr=0.0001)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"EPOCHS = 5","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"running_acc = 0\nfor epoch in range(0, EPOCHS):\n    print('\\nTraining: \\n')\n    model.train()\n    for b, (x, y) in enumerate(train_dataloader):\n\n        opt.zero_grad()\n\n        y_pred = model(x.to(device))\n        loss = criterion(y_pred, y.to(device))\n        loss.backward()\n\n        opt.step()\n        \n        acc = get_accuracy(y_pred, y.cpu())\n        \n        running_acc = running_acc * 0.9 + acc * 0.1\n\n        print('\\rEpoch: {}/{}, \\\n        batch: {}/{}, \\\n        loss: {:4f}, \\\n        running_acc: {:.4f}'.format(epoch+1, EPOCHS, b+1, len(train_dataloader), loss.item(), running_acc),  end=' ')\n    \n    print('\\nValidation: \\n')\n    running_acc = 0\n    mean_acc = 0\n    model.eval()\n    for b, (x, y) in enumerate(val_dataloader):\n\n\n        y_pred = model(x.to(device))\n\n        loss = criterion(y_pred, y.to(device))\n        acc = get_accuracy(y_pred, y)\n\n        running_acc = running_acc * 0.9 + acc * 0.1\n        mean_acc = mean_acc + acc\n\n        print('\\rbatch: {}/{}, \\\n        loss: {:4f}, \\\n        acc: {:.4f}'.format(b+1, len(val_dataloader), loss.item(), running_acc),  end=' ')\n    mean_acc /= len(val_dataloader)\n    print('Mean accuracy: ', mean_acc)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Save model after training","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"torch.save(model.state_dict(), 'birdsong_model.pth')","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}