{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":21669,"databundleVersionId":1692278,"sourceType":"competition"}],"dockerImageVersionId":31192,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install --upgrade pip","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-23T17:50:53.311715Z","iopub.execute_input":"2025-11-23T17:50:53.311893Z","iopub.status.idle":"2025-11-23T17:50:59.563127Z","shell.execute_reply.started":"2025-11-23T17:50:53.311875Z","shell.execute_reply":"2025-11-23T17:50:59.562149Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!pip install timm==0.4.12","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-23T17:50:59.565220Z","iopub.execute_input":"2025-11-23T17:50:59.565465Z","iopub.status.idle":"2025-11-23T17:52:15.139858Z","shell.execute_reply.started":"2025-11-23T17:50:59.565441Z","shell.execute_reply":"2025-11-23T17:52:15.138911Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\nimport librosa\nimport random\nimport numpy as np\nfrom tqdm import tqdm\n\nfrom skimage.transform import resize\nfrom PIL import Image\n\nimport torch\nimport torch.utils.data as torchdata\nfrom sklearn.model_selection import StratifiedKFold\nimport torch.nn as nn\nimport timm","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-23T17:52:15.141021Z","iopub.execute_input":"2025-11-23T17:52:15.141318Z","iopub.status.idle":"2025-11-23T17:52:25.000779Z","shell.execute_reply.started":"2025-11-23T17:52:15.141283Z","shell.execute_reply":"2025-11-23T17:52:25.000182Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings('ignore')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-23T17:52:25.001594Z","iopub.execute_input":"2025-11-23T17:52:25.002061Z","iopub.status.idle":"2025-11-23T17:52:25.006143Z","shell.execute_reply.started":"2025-11-23T17:52:25.002035Z","shell.execute_reply":"2025-11-23T17:52:25.005410Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Получение спектрограмм","metadata":{}},{"cell_type":"code","source":"TRAIN_TP = '/kaggle/input/rfcx-species-audio-detection/train_tp.csv'\nAUDIO_DATA = '/kaggle/input/rfcx-species-audio-detection/train/'\nWORKING_DIR = '/kaggle/working/'","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-23T17:52:25.006897Z","iopub.execute_input":"2025-11-23T17:52:25.007261Z","iopub.status.idle":"2025-11-23T17:52:25.031170Z","shell.execute_reply.started":"2025-11-23T17:52:25.007232Z","shell.execute_reply":"2025-11-23T17:52:25.030545Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"fft = 2048\nhop = 512\nsr = 48000\nlength = 10 * sr\nsave_to_disk = False\n\ndf = pd.read_csv(TRAIN_TP)\n\nfmin = int(df['f_min'].min() * 0.9)\nfmax = int(df['f_max'].max() * 1.1)\n\nfor idx, row in tqdm(df.iterrows(), total=len(df), desc='Получение спектрограмм'):\n    wav, sr = librosa.load(f\"{AUDIO_DATA}{row['recording_id']}.flac\", sr=None)\n    \n    t_min = float(row['t_min']) * sr\n    t_max = float(row['t_max']) * sr\n    \n    center = np.round((t_min + t_max) / 2)\n    beginning = center - length / 2\n    if beginning < 0:\n        beginning = 0\n    \n    ending = beginning + length\n    if ending > len(wav):\n        ending = len(wav)\n        beginning = ending - length\n        \n    slice = wav[int(beginning):int(ending)]\n    \n    mel_spec = librosa.feature.melspectrogram(y=slice, n_fft=fft, hop_length=hop, sr=sr, fmin=fmin, fmax=fmax, power=1.5)\n    mel_spec = resize(mel_spec, (224, 400))\n    \n    mel_spec = mel_spec - np.min(mel_spec)\n    mel_spec = mel_spec / np.max(mel_spec)\n\n    mel_spec = mel_spec * 255\n    mel_spec = np.round(mel_spec)    \n    mel_spec = mel_spec.astype('uint8')\n    mel_spec = np.asarray(mel_spec)\n    \n    bmp = Image.fromarray(mel_spec, 'L')\n    bmp.save(f\"{WORKING_DIR}{row['recording_id']}_{row['species_id']}_{int(center)}.bmp\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-11-23T17:52:25.031995Z","iopub.execute_input":"2025-11-23T17:52:25.032320Z","iopub.status.idle":"2025-11-23T17:54:59.551781Z","shell.execute_reply.started":"2025-11-23T17:52:25.032299Z","shell.execute_reply":"2025-11-23T17:54:59.550916Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"num_birds = 24\nbatch_size = 16\n\nrng_seed = 1234\nrandom.seed(rng_seed)\nnp.random.seed(rng_seed)\nos.environ['PYTHONHASHSEED'] = str(rng_seed)\ntorch.manual_seed(rng_seed)\ntorch.cuda.manual_seed(rng_seed)\ntorch.backends.cudnn.deterministic = True\ntorch.backends.cudnn.benchmark = False","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-23T17:54:59.554201Z","iopub.execute_input":"2025-11-23T17:54:59.554660Z","iopub.status.idle":"2025-11-23T17:54:59.564535Z","shell.execute_reply.started":"2025-11-23T17:54:59.554639Z","shell.execute_reply":"2025-11-23T17:54:59.563819Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Датасет","metadata":{}},{"cell_type":"code","source":"class RainforestDataset(torchdata.Dataset):\n    def __init__(self, filelist):\n        self.specs = []\n        self.labels = []\n        for f in filelist:\n            label = int(str.split(f, '_')[1])\n            label_array = np.zeros(num_birds, dtype=np.single)\n            label_array[label] = 1.\n            self.labels.append(label_array)\n\n            img = Image.open(WORKING_DIR + f)\n            mel_spec = np.array(img)\n            img.close()\n\n            mel_spec = mel_spec / 255\n            mel_spec = np.stack((mel_spec, mel_spec, mel_spec))\n            \n            self.specs.append(mel_spec)\n    \n    def __len__(self):\n        return len(self.specs)\n    \n    def __getitem__(self, item):\n        return self.specs[item], self.labels[item]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-23T17:54:59.565361Z","iopub.execute_input":"2025-11-23T17:54:59.565662Z","iopub.status.idle":"2025-11-23T17:54:59.576136Z","shell.execute_reply.started":"2025-11-23T17:54:59.565636Z","shell.execute_reply":"2025-11-23T17:54:59.575425Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"file_list = []\nlabel_list = []\n\nfor f in os.listdir(WORKING_DIR):\n    if '.bmp' in f:\n        file_list.append(f)\n        label = str.split(f, '_')[1]\n        label_list.append(label)\n\n\nskf = StratifiedKFold(n_splits=5, shuffle=True, random_state=rng_seed)\n\ntrain_files = []\nval_files = []\n\nfor fold_id, (train_index, val_index) in enumerate(skf.split(file_list, label_list)):\n    if fold_id == 0:\n        train_files = np.take(file_list, train_index)\n        val_files = np.take(file_list, val_index)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-23T17:54:59.576899Z","iopub.execute_input":"2025-11-23T17:54:59.577210Z","iopub.status.idle":"2025-11-23T17:54:59.600213Z","shell.execute_reply.started":"2025-11-23T17:54:59.577191Z","shell.execute_reply":"2025-11-23T17:54:59.599302Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Обучение","metadata":{}},{"cell_type":"code","source":"train_dataset = RainforestDataset(train_files)\nval_dataset = RainforestDataset(val_files)\n\ntrain_loader = torchdata.DataLoader(train_dataset, batch_size=batch_size, sampler=torchdata.RandomSampler(train_dataset))\nval_loader = torchdata.DataLoader(val_dataset, batch_size=batch_size, sampler=torchdata.RandomSampler(val_dataset))\n\nmodel = timm.create_model('resnest101e', pretrained=True)\n\nmodel.fc = nn.Sequential(\n    nn.Linear(2048, 1024),\n    nn.ReLU(),\n    nn.Dropout(p=0.2),\n    nn.Linear(1024, 1024),\n    nn.ReLU(),\n    nn.Dropout(p=0.2),\n    nn.Linear(1024, num_birds)\n)\n\noptimizer = torch.optim.SGD(model.parameters(), lr=0.01, weight_decay=0.0001, momentum=0.9)\nscheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.4)\n\npos_weights = torch.ones(num_birds)\npos_weights = pos_weights * num_birds\nloss_function = nn.BCEWithLogitsLoss(pos_weight=pos_weights)\n\nif torch.cuda.is_available():\n    model = model.cuda()\n    loss_function = loss_function.cuda()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-23T17:54:59.601214Z","iopub.execute_input":"2025-11-23T17:54:59.601760Z","iopub.status.idle":"2025-11-23T17:55:05.563980Z","shell.execute_reply.started":"2025-11-23T17:54:59.601734Z","shell.execute_reply":"2025-11-23T17:55:05.563137Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"best_corrects = 0\n\nfor e in tqdm(range(0, 20), desc='Эпоха'):\n    train_loss = []\n    train_corr = []\n\n    model.train()\n    for batch, (data, target) in enumerate(train_loader):\n        data = data.float()\n        if torch.cuda.is_available():\n            data, target = data.cuda(), target.cuda()\n            \n        optimizer.zero_grad()\n        \n        output = model(data)\n        loss = loss_function(output, target)\n        \n        loss.backward()\n        optimizer.step()\n\n        vals, answers = torch.max(output, 1)\n        vals, targets = torch.max(target, 1)\n        corrects = 0\n        for i in range(0, len(answers)):\n            if answers[i] == targets[i]:\n                corrects = corrects + 1\n        train_corr.append(corrects)\n        \n        train_loss.append(loss.item())\n\n    for g in optimizer.param_groups:\n        lr = g['lr']\n\n    with torch.no_grad():\n        # Stats\n        val_loss = []\n        val_corr = []\n        \n        model.eval()\n        for batch, (data, target) in enumerate(val_loader):\n            data = data.float()\n            if torch.cuda.is_available():\n                data, target = data.cuda(), target.cuda()\n            \n            output = model(data)\n            loss = loss_function(output, target)\n\n            vals, answers = torch.max(output, 1)\n            vals, targets = torch.max(target, 1)\n            corrects = 0\n            for i in range(0, len(answers)):\n                if answers[i] == targets[i]:\n                    corrects = corrects + 1\n            val_corr.append(corrects)\n        \n            val_loss.append(loss.item())\n    '''\n    tqdm.set_postfix({\n         'train_loss': sum(train_loss) / len(train_loss), \n         'train_correct': f'{sum(train_corr)}/{len(train_dataset)}',\n         'val_loss': str(sum(val_loss) / len(val_loss)), \n         'val_correct': str(sum(val_corr)) + '/' + str(val_dataset.__len__()),\n     })\n    '''\n    if sum(val_corr) > best_corrects:\n        print('Saving new best model at epoch ' + str(e) + ' (' + str(sum(val_corr)) + '/' + str(val_dataset.__len__()) + ')')\n        torch.save(model, 'best_model.pt')\n        best_corrects = sum(val_corr)\n\n    scheduler.step()\n\ndel model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-23T18:04:09.862743Z","iopub.execute_input":"2025-11-23T18:04:09.863093Z","iopub.status.idle":"2025-11-23T18:05:51.436772Z","shell.execute_reply.started":"2025-11-23T18:04:09.863071Z","shell.execute_reply":"2025-11-23T18:05:51.436040Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_test_file(f):\n    wav, sr = librosa.load('/kaggle/input/rfcx-species-audio-detection/test/' + f, sr=None)\n\n    segments = len(wav) / length\n    segments = int(np.ceil(segments))\n    \n    mel_array = []\n    \n    for i in range(0, segments):\n        if (i + 1) * length > len(wav):\n            slice = wav[len(wav) - length:len(wav)]\n        else:\n            slice = wav[i * length:(i + 1) * length]\n\n        mel_spec = librosa.feature.melspectrogram(y=slice, n_fft=fft, hop_length=hop, sr=sr, fmin=fmin, fmax=fmax, power=1.5)\n        mel_spec = resize(mel_spec, (224, 400))\n    \n        mel_spec = mel_spec - np.min(mel_spec)\n        mel_spec = mel_spec / np.max(mel_spec)\n        \n        mel_spec = np.stack((mel_spec, mel_spec, mel_spec))\n\n        mel_array.append(mel_spec)\n    \n    return mel_array","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-23T18:08:14.547647Z","iopub.execute_input":"2025-11-23T18:08:14.547977Z","iopub.status.idle":"2025-11-23T18:08:14.554343Z","shell.execute_reply.started":"2025-11-23T18:08:14.547952Z","shell.execute_reply":"2025-11-23T18:08:14.553558Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Тестирование","metadata":{}},{"cell_type":"code","source":"model = timm.create_model('resnest101e', pretrained=True)\n\nmodel.fc = nn.Sequential(\n    nn.Linear(2048, 1024),\n    nn.ReLU(),\n    nn.Dropout(p=0.2),\n    nn.Linear(1024, 1024),\n    nn.ReLU(),\n    nn.Dropout(p=0.2),\n    nn.Linear(1024, num_birds)\n)\n\nmodel = torch.load(WORKING_DIR + 'best_model.pt', weights_only=False)\nmodel.eval()\n\nif not save_to_disk:\n    for f in os.listdir(WORKING_DIR):\n        os.remove(WORKING_DIR + f)\n\nif torch.cuda.is_available():\n    model.cuda()\n    \nresults = []\n\ntest_files = os.listdir('/kaggle/input/rfcx-species-audio-detection/test/')\n\nfor file_name in tqdm(test_files, desc='Processing test files'):\n    data = load_test_file(file_name)\n    data = torch.tensor(data).float()\n    if torch.cuda.is_available():\n        data = data.cuda()\n\n    output = model(data)\n    maxed_output = torch.max(output, dim=0)[0].cpu().detach().numpy()\n\n    file_id = file_name.split('.')[0]\n    row = [file_id] + maxed_output.tolist()\n    results.append(row)\n\ncolumns = ['recording_id'] + [f's{i}' for i in range(24)]\ndf = pd.DataFrame(results, columns=columns)\ndf.to_csv('submission.csv', index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-23T18:10:27.328823Z","iopub.execute_input":"2025-11-23T18:10:27.329133Z","execution_failed":"2025-11-23T18:13:17.976Z"}},"outputs":[],"execution_count":null}]}