{"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_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"from pathlib import Path #文件路径\nimport pandas as pd\nimport torch\nfrom fastprogress import progress_bar #显示进度条\nimport numpy as np\nimport warnings\nfrom collections import defaultdict\nfrom collections import Counter#计数器","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Torch目标是让你通过极其简单过程、最大的灵活性和速度建立自己的科学算法。Torch有一个在机器学习领域大型生态社区驱动库包，包括计算机视觉软件包，信号处理，并行处理，图像，视频，音频和网络等，基于Lua社区建立。\n\nTorch 的核心是流行的神经网络，它使用简单的优化库，同时具有最大的灵活性，实现复杂的神经网络的拓扑结构。你可以建立神经网络和并行任意图，通过CPU和GPU等有效方式。\n\ndict和defaultdict的区别：普通的字典时，用法一般是dict={},添加元素的只需要dict[element] =value即，调用的时候也是如此，dict[element] = xxx,但前提是element字典里，如果不在字典里就会报错\ndefaultdict作用是在于，当字典里的key不存在但被查找时，返回的不是keyError而是对应的默认值，比如list对应[ ]，str对应的是空字符串，set对应set( )，int对应0等","metadata":{}},{"cell_type":"code","source":"torch.manual_seed(42)##为CPU设置种子用于生成随机数，以使得结果是确定的\ntorch.backends.cudnn.deterministic = True\ntorch.backends.cudnn.benchmark = False\nnp.random.seed(42)","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"cuDNN 是英伟达专门为深度神经网络所开发出来的 GPU 加速库，针对卷积、池化等等常见操作做了非常多的底层优化，比一般的 GPU 程序要快很多\ntorch.backends.cudnn.deterministic是啥？顾名思义，将这个 flag 置为True的话，每次返回的卷积算法将是确定的，即默认算法。如果配合上设置 Torch 的随机种子为固定值的话，可以保证每次运行网络的时候相同输入的输出是固定的\n使用 cuDNN 的时候，torch.backends.cudnn.benchmark 模式是为 False","metadata":{}},{"cell_type":"markdown","source":"# 1. Prelim","metadata":{}},{"cell_type":"code","source":"ROOT = Path.cwd().parent #获取上层目录\nINPUT_ROOT = ROOT / \"input\"\nRAW_DATA = INPUT_ROOT / \"birdsong-recognition\"\nTRAIN_AUDIO_DIR = RAW_DATA / \"train_audio\"\nTEST_AUDIO_DIR = RAW_DATA / \"test_audio\"\n\nif not TEST_AUDIO_DIR.exists():\n    TEST_AUDIO_DIR = INPUT_ROOT / \"birdcall-check\" / \"test_audio\"\n    test = pd.read_csv(INPUT_ROOT / \"birdcall-check\" / \"test.csv\")\nelse:\n    test = pd.read_csv(RAW_DATA / \"test.csv\")\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")","metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"cuda 统一计算设备架构（Compute Unified Device Architecture, CUDA），是由NVIDIA推出的通用并行计算架构。解决的是用更加廉价的设备资源，实现更高效的并行计算","metadata":{}},{"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()}#遍历bird_code列表","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 2. Models","metadata":{}},{"cell_type":"markdown","source":"## 2.1 Audio Utils\n","metadata":{}},{"cell_type":"markdown","source":"DFT：离散傅里叶变换\n离散傅里叶变换可以将连续的频谱转化成离散的频谱去计算，这样就易于计算机编程实现傅里叶变换的计算\n\nIDFT：离散傅里叶逆变换\n\nclass torch.nn.Conv1d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True)\n\nin_channels(int) – 输入信号的通道。\nout_channels(int) – 卷积产生的通道。有多少个out_channels，就需要多少个1维卷积\nkernel_size(int or tuple) - 卷积核的尺寸，卷积核的大小为(k,)，第二个维度是由in_channels来决定的，所以实际上卷积大小为kernel_size*in_channels\n\nstride(int or tuple, optional) - 卷积步长。\npadding (int or tuple, optional)- 输入的每一条边补充0的层数。\ndilation(int or tuple, `optional``) – 卷积核元素之间的间距。\ngroups(int, optional) – 从输入通道到输出通道的阻塞连接数。\nbias(bool, optional) - 如果bias=True，添加偏置。","metadata":{}},{"cell_type":"code","source":"\nimport torch.nn as nn #包含大量的损失函数与激活函数和用于构建网络的函数。\nimport numpy as np\nimport torch \nimport librosa # 音频处理库\nimport torch.nn.functional as F#用 torch.nn.functional中带有的损失函数来代替我们自己编写的函数，使得代码变得更简短\nclass DFTBase(nn.Module):\n    def __init__(self):\n        \"\"\"Base class for DFT and IDFT matrix\"\"\"\n        super(DFTBase, self).__init__()\n\n    def dft_matrix(self, n):#DFT正变换，可使用内置函数dftmtx(n)\n        (x, y) = np.meshgrid(np.arange(n), np.arange(n))#生成网格点坐标矩阵\n        omega = np.exp(-2 * np.pi * 1j / n)\n        W = np.power(omega, x * y)\n        return W\n\n    def idft_matrix(self, n):\n        (x, y) = np.meshgrid(np.arange(n), np.arange(n))\n        omega = np.exp(2 * np.pi * 1j / n)\n        W = np.power(omega, x * y)\n        return W\n    \n    \nclass STFT(DFTBase):\n    def __init__(self, n_fft=2048, hop_length=None, win_length=None, \n        window='hann', center=True, pad_mode='reflect', freeze_parameters=True):\n        \"\"\"用Conv1d（卷积神经网络）实现STFT。该函数具有librosa.core.stft的相同输出\n        Conv1D (batch, steps, channels)，steps表示1篇文本中含有的单词数量，channels表示1个单词的维度\n        STFT短时傅里叶变换，对一系列加窗数据做FFT(离散傅氏变换（DFT）的快速算法)。\n        \"\"\"\n        super(STFT, self).__init__()#对继承自父类的属性进行初始化\n\n        assert pad_mode in ['constant', 'reflect'] #pad_mode：填充模式\n\n        self.n_fft = n_fft #n_fft：窗口大小\n        self.center = center\n        self.pad_mode = pad_mode\n\n        # win_length 每一帧音频都由window（）加窗。窗长win_length，然后用零填充以匹配N_FFT。默认win_length=n_fft。\n        #window：字符串，元组，数字，函数 shape =（n_fft, )\n        if win_length is None:\n            win_length = n_fft\n\n        #hop_length：帧移（音频样本数） 设置默认跃点（如果尚未指定）\n        if hop_length is None:\n            hop_length = int(win_length // 4)\n\n        fft_window = librosa.filters.get_window(window, win_length, fftbins=True)#窗函数的调用\n\n        #将窗口填充到n_fft大小\n        fft_window = librosa.util.pad_center(fft_window, n_fft)\n\n        # DFT & IDFT matrix\n        self.W = self.dft_matrix(n_fft)\n\n        out_channels = n_fft // 2 + 1\n\n        self.conv_real = nn.Conv1d(in_channels=1, out_channels=out_channels, \n            kernel_size=n_fft, stride=hop_length, padding=0, dilation=1, \n            groups=1, bias=False)\n\n        self.conv_imag = nn.Conv1d(in_channels=1, out_channels=out_channels, \n            kernel_size=n_fft, stride=hop_length, padding=0, dilation=1, \n            groups=1, bias=False)\n\n        self.conv_real.weight.data = torch.Tensor(\n            np.real(self.W[:, 0 : out_channels] * fft_window[:, None]).T)[:, None, :]\n        # (n_fft // 2 + 1, 1, n_fft)\n\n        self.conv_imag.weight.data = torch.Tensor(\n            np.imag(self.W[:, 0 : out_channels] * fft_window[:, None]).T)[:, None, :]\n        # (n_fft // 2 + 1, 1, n_fft)\n\n        if freeze_parameters:\n            for param in self.parameters():\n                param.requires_grad = False\n\n    def forward(self, input):\n        \"\"\"input: (batch_size, data_length)\n        Returns:\n          real: (batch_size, n_fft // 2 + 1, time_steps)\n          imag: (batch_size, n_fft // 2 + 1, time_steps)\n        \"\"\"\n\n        x = input[:, None, :]   # (batch_size, channels_num, data_length)\n\n        if self.center:\n            x = F.pad(x, pad=(self.n_fft // 2, self.n_fft // 2), mode=self.pad_mode)\n\n        real = self.conv_real(x)\n        imag = self.conv_imag(x)\n        # (batch_size, n_fft // 2 + 1, time_steps)\n\n        real = real[:, None, :, :].transpose(2, 3)#交换维度\n        imag = imag[:, None, :, :].transpose(2, 3)\n        # (batch_size, 1, time_steps, n_fft // 2 + 1)\n\n        return real, imag#函数的实部、虚部\n    \n    #  spectrogram是一个MATLAB函数，使用短时傅里叶变换得到信号的频谱图。\nclass Spectrogram(nn.Module):\n    def __init__(self, n_fft=2048, hop_length=None, win_length=None, \n        window='hann', center=True, pad_mode='reflect', power=2.0, \n        freeze_parameters=True):\n        \"\"\"使用pytorch计算频谱图。STFT通过Conv1d实现\n        \"\"\"\n        super(Spectrogram, self).__init__()\n\n        self.power = power\n\n        self.stft = STFT(n_fft=n_fft, hop_length=hop_length, \n            win_length=win_length, window=window, center=center, \n            pad_mode=pad_mode, freeze_parameters=True)\n\n    def forward(self, input):\n        \"\"\"input: (batch_size, 1, time_steps, n_fft // 2 + 1)\n        Returns:\n          spectrogram: (batch_size, 1, time_steps, n_fft // 2 + 1)\n        \"\"\"\n\n        (real, imag) = self.stft.forward(input)\n        # (batch_size, n_fft // 2 + 1, time_steps)\n\n        spectrogram = real ** 2 + imag ** 2\n\n        if self.power == 2.0:\n            pass\n        else:\n            spectrogram = spectrogram ** (power / 2.0)\n\n        return spectrogram\n\n    #提取Log-Mel Spectrogram 特征 Log-Mel Spectrogram特征是目前在语音识别和环境声音识别中很常用的一个特征\nclass LogmelFilterBank(nn.Module):\n    def __init__(self, sr=32000, n_fft=2048, n_mels=64, fmin=50, fmax=14000, is_log=True, \n        ref=1.0, amin=1e-10, top_db=80.0, freeze_parameters=True):\n        \"\"\"使用pytorch计算logmel频谱图。mel过滤器库是基于 librosa.filters.mel的pytorch实现\n        self：要显示的矩阵\n        sr：采样率\n        n_fft ：FFT组件数\n        频率类型：'is_log'：频谱以对数刻度显示  'mel'：频率由mel标度决定，n_mels ：产生的梅尔带数\n        fmin ：最低频率（Hz）\n        fmax：最高频率（以Hz为单位）\n        ref ：参考值\n        \"\"\"\n        super(LogmelFilterBank, self).__init__()\n\n        self.is_log = is_log\n        self.ref = ref\n        self.amin = amin\n        self.top_db = top_db\n\n        self.melW = librosa.filters.mel(sr=sr, n_fft=n_fft, n_mels=n_mels,\n            fmin=fmin, fmax=fmax).T\n        # (n_fft // 2 + 1, mel_bins)\n\n        self.melW = nn.Parameter(torch.Tensor(self.melW))\n\n        if freeze_parameters:\n            for param in self.parameters():\n                param.requires_grad = False\n\n    def forward(self, input):\n        \"\"\"input: (batch_size, channels, time_steps)\n        \n        Output: (batch_size, time_steps, mel_bins)\n        \"\"\"\n\n        # Mel spectrogram\n        mel_spectrogram = torch.matmul(input, self.melW)\n\n        # Logmel spectrogram\n        if self.is_log:\n            output = self.power_to_db(mel_spectrogram)\n        else:\n            output = mel_spectrogram\n\n        return output\n\n         #功率转dB\n    def power_to_db(self, input):\n        \"\"\"\n        基于librosa.core.power_to_lb实现 将功率谱（幅度平方）转换为分贝（dB）单位\n        \"\"\"\n        ref_value = self.ref\n        log_spec = 10.0 * torch.log10(torch.clamp(input, min=self.amin, max=np.inf))\n        log_spec -= 10.0 * np.log10(np.maximum(self.amin, ref_value))\n\n        if self.top_db is not None:\n            if self.top_db < 0:\n                raise ParameterError('top_db must be non-negative')\n            log_spec = torch.clamp(log_spec, min=log_spec.max().item() - self.top_db, max=np.inf)\n\n        return log_spec\n\n\nclass DropStripes(nn.Module):\n    def __init__(self, dim, drop_width, stripes_num):\n        \"\"\"下降条纹. \n        Args:\n          dim: int, 沿其下降的尺寸\n          drop_width: int, 下降的最大条纹宽度\n          stripes_num: int, how many stripes to drop\n        \"\"\"\n        super(DropStripes, self).__init__()\n\n        assert dim in [2, 3]    # dim 2: time; dim 3: frequency\n\n        self.dim = dim\n        self.drop_width = drop_width\n        self.stripes_num = stripes_num\n\n    def forward(self, input):\n        \"\"\"input: (batch_size, channels, time_steps, freq_bins)\"\"\"\n\n        assert input.ndimension() == 4\n\n        if self.training is False:\n            return input\n\n        else:\n            batch_size = input.shape[0]\n            total_width = input.shape[self.dim]\n\n            for n in range(batch_size):\n                self.transform_slice(input[n], total_width)\n\n            return input\n\n\n    def transform_slice(self, e, total_width):\n        \"\"\"e: (channels, time_steps, freq_bins)\"\"\"\n\n        for _ in range(self.stripes_num):\n            distance = torch.randint(low=0, high=self.drop_width, size=(1,))[0]#返回均匀分布的[low,high]之间的整数随机值\n            bgn = torch.randint(low=0, high=total_width - distance, size=(1,))[0]\n\n            if self.dim == 2:\n                e[:, bgn : bgn + distance, :] = 0\n            elif self.dim == 3:\n                e[:, :, bgn : bgn + distance] = 0\n\n\nclass SpecAugmentation(nn.Module):\n    def __init__(self, time_drop_width, time_stripes_num, freq_drop_width, \n        freq_stripes_num):\n        \"\"\"Spec augmetation. 一种新数据增强方法\n          time_drop_width: int\n          time_stripes_num: int\n          freq_drop_width: int\n          freq_stripes_num: int\n        \"\"\"\n\n        super(SpecAugmentation, self).__init__()\n\n        self.time_dropper = DropStripes(dim=2, drop_width=time_drop_width, \n            stripes_num=time_stripes_num)#时间变形\n\n        self.freq_dropper = DropStripes(dim=3, drop_width=freq_drop_width, \n            stripes_num=freq_stripes_num)#频率变形\n\n    def forward(self, input):\n        x = self.time_dropper(input)\n        x = self.freq_dropper(x)\n        return x","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2.2 PANN Models","metadata":{}},{"cell_type":"code","source":"\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\nimport torchvision.models as models#torchvision图像视频处理\n\ndef init_layer(layer):\n    nn.init.xavier_uniform_(layer.weight)#服从均匀分布\n\n    if hasattr(layer, \"bias\"):\n        if layer.bias is not None:\n            layer.bias.data.fill_(0.)\n\n\ndef init_bn(bn):\n    bn.bias.data.fill_(0.)\n    bn.weight.data.fill_(1.0)\n\n\ndef interpolate(x: torch.Tensor, ratio: int):\n    \"\"\"在时域内插数据。这用于补偿CNN下采样时分辨率的降低。\n\n    Args:\n      x: (batch_size, time_steps, classes_num)\n      ratio: int, 插值比例\n    Returns:\n      upsampled: (batch_size, time_steps * ratio, classes_num)\n    \"\"\"\n    (batch_size, time_steps, classes_num) = x.shape\n    upsampled = x[:, :, None, :].repeat(1, 1, ratio, 1)\n    upsampled = upsampled.reshape(batch_size, time_steps * ratio, classes_num)\n    return upsampled\n\n\ndef pad_framewise_output(framewise_output: torch.Tensor, frames_num: int):\n    \"\"\"将framewise_output填充到与输入帧相同的长度。填充值与最后一帧的值相同。\n    Args:\n      framewise_output: (batch_size, frames_num, classes_num)\n      frames_num: int, number of frames to pad\n    Outputs:\n      output: (batch_size, frames_num, classes_num)\n    \"\"\"\n    pad = framewise_output[:, -1:, :].repeat(\n        1, frames_num - framewise_output.shape[1], 1)\n    \"\"\"tensor for padding\"\"\"\n\n    output = torch.cat((framewise_output, pad), dim=1)\n    \"\"\"(batch_size, frames_num, classes_num)\"\"\"\n\n    return output\n\n    #卷积神经网络\nclass ConvBlock(nn.Module):\n    def __init__(self, in_channels: int, out_channels: int):\n        super().__init__()\n\n        self.conv1 = nn.Conv2d(\n            in_channels=in_channels,\n            out_channels=out_channels,\n            kernel_size=(3, 3),\n            stride=(1, 1),\n            padding=(1, 1),\n            bias=False)\n\n        self.conv2 = nn.Conv2d(\n            in_channels=out_channels,\n            out_channels=out_channels,\n            kernel_size=(3, 3),\n            stride=(1, 1),\n            padding=(1, 1),\n            bias=False)\n\n        self.bn1 = nn.BatchNorm2d(out_channels)\n        self.bn2 = nn.BatchNorm2d(out_channels)\n\n        self.init_weight()\n\n    def init_weight(self):\n        init_layer(self.conv1)\n        init_layer(self.conv2)\n        init_bn(self.bn1)\n        init_bn(self.bn2)\n\n    def forward(self, input, pool_size=(2, 2), pool_type='avg'):\n\n        x = input\n        x = F.relu_(self.bn1(self.conv1(x)))\n        x = F.relu_(self.bn2(self.conv2(x)))\n        if pool_type == 'max':\n            x = F.max_pool2d(x, kernel_size=pool_size)\n        elif pool_type == 'avg':\n            x = F.avg_pool2d(x, kernel_size=pool_size)\n        elif pool_type == 'avg+max':\n            x1 = F.avg_pool2d(x, kernel_size=pool_size)\n            x2 = F.max_pool2d(x, kernel_size=pool_size)\n            x = x1 + x2\n        else:\n            raise Exception('Incorrect argument!')\n\n        return x\n\n      #卷积块注意模块，一个简单而有效的注意模块的前馈卷积神经网络\nclass AttBlock(nn.Module):\n    def __init__(self,\n                 in_features: int,\n                 out_features: int,\n                 activation=\"linear\",\n                 temperature=1.0):\n        super().__init__()\n\n        self.activation = activation\n        self.temperature = temperature\n        self.att = nn.Conv1d(\n            in_channels=in_features,\n            out_channels=out_features,\n            kernel_size=1,\n            stride=1,\n            padding=0,\n            bias=True)\n        self.cla = nn.Conv1d(\n            in_channels=in_features,\n            out_channels=out_features,\n            kernel_size=1,\n            stride=1,\n            padding=0,\n            bias=True)\n\n        self.bn_att = nn.BatchNorm1d(out_features)\n        self.init_weights()\n\n    def init_weights(self):\n        init_layer(self.att)\n        init_layer(self.cla)\n        init_bn(self.bn_att)\n\n    def forward(self, x):\n        # x: (n_samples, n_in, n_time)\n        norm_att = torch.softmax(torch.tanh(self.att(x)), dim=-1)\n        cla = self.nonlinear_transform(self.cla(x))\n        x = torch.sum(norm_att * cla, dim=2)\n        return x, norm_att, cla\n\n    def nonlinear_transform(self, x):\n        if self.activation == 'linear':\n            return x\n        elif self.activation == 'sigmoid':\n            return torch.sigmoid(x)\n        #模型拟合\nclass PANNsDense121Att(nn.Module):\n    def __init__(self, sample_rate: int, window_size: int, hop_size: int,\n                 mel_bins: int, fmin: int, fmax: int, classes_num: int, apply_aug: bool, top_db=None):\n        super().__init__()\n        \n        window = 'hann'\n        center = True\n        pad_mode = 'reflect'\n        ref = 1.0\n        amin = 1e-10\n        self.interpolate_ratio = 32  # Downsampled ratio\n        self.apply_aug = apply_aug\n\n        #频谱图提取器\n        self.spectrogram_extractor = Spectrogram(\n            n_fft=window_size,\n            hop_length=hop_size,\n            win_length=window_size,\n            window=window,\n            center=center,\n            pad_mode=pad_mode,\n            freeze_parameters=True)\n\n        # Logmel特征提取器\n        self.logmel_extractor = LogmelFilterBank(\n            sr=sample_rate,\n            n_fft=window_size,\n            n_mels=mel_bins,\n            fmin=fmin,\n            fmax=fmax,\n            ref=ref,\n            amin=amin,\n            top_db=top_db,\n            freeze_parameters=True)\n\n        # Spec augmenter 增强数据\n        self.spec_augmenter = SpecAugmentation(\n            time_drop_width=64,\n            time_stripes_num=2,\n            freq_drop_width=8,\n            freq_stripes_num=2)\n\n        self.bn0 = nn.BatchNorm2d(mel_bins)\n\n        self.fc1 = nn.Linear(1024, 1024, bias=True)\n        self.att_block = AttBlock(1024, classes_num, activation='sigmoid')\n\n\n        self.densenet_features = models.densenet121(pretrained=False).features\n\n        self.init_weight()\n\n    def init_weight(self):\n        init_bn(self.bn0)\n        init_layer(self.fc1)\n        #cnn特征提取\n    def cnn_feature_extractor(self, x):\n        x = self.densenet_features(x)\n        return x\n        #预处理\n    def preprocess(self, input_x, mixup_lambda=None):\n\n        x = self.spectrogram_extractor(input_x)  # (batch_size, 1, time_steps, freq_bins)\n        x = self.logmel_extractor(x)  # (batch_size, 1, time_steps, mel_bins)\n\n        frames_num = x.shape[2]\n\n        x = x.transpose(1, 3)\n        x = self.bn0(x)\n        x = x.transpose(1, 3)\n\n        if self.apply_aug:\n            x = self.spec_augmenter(x)\n\n        return x, frames_num\n        \n        \n    def forward(self, input_data):\n        input_x, mixup_lambda = input_data\n        \"\"\"\n        Input: (batch_size, data_length)\"\"\"\n        b, c, s = input_x.shape\n        input_x = input_x.reshape(b*c, s)\n        x, frames_num = self.preprocess(input_x, mixup_lambda=mixup_lambda)\n        if mixup_lambda is not None:\n            b = (b*c)//2\n            c = 1\n        # Output shape (batch size, channels, time, frequency)\n        x = x.expand(x.shape[0], 3, x.shape[2], x.shape[3])\n        x = self.cnn_feature_extractor(x)\n        \n        #汇总在频率轴上\n        x = torch.mean(x, dim=3)\n\n        x1 = F.max_pool1d(x, kernel_size=3, stride=1, padding=1)\n        x2 = F.avg_pool1d(x, kernel_size=3, stride=1, padding=1)\n        x = x1 + x2\n\n        x = F.dropout(x, p=0.5, training=self.training)\n        x = x.transpose(1, 2)\n        x = F.relu_(self.fc1(x))\n        x = x.transpose(1, 2)\n        x = F.dropout(x, p=0.5, training=self.training)\n\n        (clipwise_output, norm_att, segmentwise_output) = self.att_block(x)\n        segmentwise_output = segmentwise_output.transpose(1, 2)\n\n        # 获取逐帧输出\n        framewise_output = interpolate(segmentwise_output,\n                                       self.interpolate_ratio)\n        framewise_output = pad_framewise_output(framewise_output, frames_num)\n        frame_shape =  framewise_output.shape\n        clip_shape = clipwise_output.shape\n        output_dict = {\n            'framewise_output': framewise_output.reshape(b, c, frame_shape[1],frame_shape[2]),\n            'clipwise_output': clipwise_output.reshape(b, c, clip_shape[1]),\n        }\n\n        return output_dict","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 2.3 Model Utils","metadata":{}},{"cell_type":"code","source":"def get_model(ModelClass: object, config: dict, weights_path: str):\n    model = ModelClass(**config)\n    checkpoint = torch.load(weights_path, map_location='cpu')\n    model.load_state_dict(checkpoint[\"model\"])\n    model.to(device)\n    model.eval()\n    return model","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 3. Model Parameters","metadata":{}},{"cell_type":"code","source":"list_of_models = [\n    {\n        \"model_class\": PANNsDense121Att,\n        \"config\": {\n            \"sample_rate\": 32000,\n            \"window_size\": 1024,\n            \"hop_size\": 320,\n            \"mel_bins\": 64,\n            \"fmin\": 50,\n            \"fmax\": 14000,\n            \"classes_num\": 264,\n            \"apply_aug\": True,\n            \"top_db\": None\n        },\n        \"weights_path\": \"../input/birdsongdetectionfinalsubmission1/final_sed_dense121_nomix_fold0_checkpoint_50_score0.7057.pt\",\n        \"clip_threshold\": 0.3,\n        \"threshold\": 0.3\n    },\n    {\n        \"model_class\": PANNsDense121Att,\n        \"config\": {\n            \"sample_rate\": 32000,\n            \"window_size\": 1024,\n            \"hop_size\": 320,\n            \"mel_bins\": 64,\n            \"fmin\": 50,\n            \"fmax\": 14000,\n            \"classes_num\": 264,\n            \"apply_aug\": True,\n            \"top_db\": None\n        },\n        \"weights_path\": \"../input/birdsongdetectionfinalsubmission1/final_sed_dense121_nomix_fold1_checkpoint_48_score0.6943.pt\",\n        \"clip_threshold\": 0.3,\n        \"threshold\": 0.3\n    },\n    {\n        \"model_class\": PANNsDense121Att,\n        \"config\": {\n            \"sample_rate\": 32000,\n            \"window_size\": 1024,\n            \"hop_size\": 320,\n            \"mel_bins\": 64,\n            \"fmin\": 50,\n            \"fmax\": 14000,\n            \"classes_num\": 264,\n            \"apply_aug\": True,\n            \"top_db\": None\n        },\n        \"weights_path\": \"../input/birdsongdetectionfinalsubmission1/final_sed_dense121_nomix_fold2_augd_checkpoint_50_score0.6666.pt\",\n        \"clip_threshold\": 0.3,\n        \"threshold\": 0.3\n    },\n    {\n        \"model_class\": PANNsDense121Att,\n        \"config\": {\n            \"sample_rate\": 32000,\n            \"window_size\": 1024,\n            \"hop_size\": 320,\n            \"mel_bins\": 64,\n            \"fmin\": 50,\n            \"fmax\": 14000,\n            \"classes_num\": 264,\n            \"apply_aug\": True,\n            \"top_db\": None\n        },\n        \"weights_path\": \"../input/birdsongdetectionfinalsubmission1/final_sed_dense121_nomix_fold3_augd_checkpoint_50_score0.6713.pt\",\n        \"clip_threshold\": 0.3,\n        \"threshold\": 0.3\n    },\n    {\n        \"model_class\": PANNsDense121Att,\n        \"config\": {\n            \"sample_rate\": 32000,\n            \"window_size\": 1024,\n            \"hop_size\": 320,\n            \"mel_bins\": 64,\n            \"fmin\": 50,\n            \"fmax\": 14000,\n            \"classes_num\": 264,\n            \"apply_aug\": True,\n            \"top_db\": None\n        },\n        \"weights_path\": \"../input/birdsongdetectionfinalsubmission1/final_5fold_sed_dense121_nomix_fold0_checkpoint_50_score0.7219.pt\",\n        \"clip_threshold\": 0.3,\n        \"threshold\": 0.3\n    },\n    {\n        \"model_class\": PANNsDense121Att,\n        \"config\": {\n            \"sample_rate\": 32000,\n            \"window_size\": 1024,\n            \"hop_size\": 320,\n            \"mel_bins\": 64,\n            \"fmin\": 50,\n            \"fmax\": 14000,\n            \"classes_num\": 264,\n            \"apply_aug\": True,\n            \"top_db\": None\n        },\n        \"weights_path\": \"../input/birdsongdetectionfinalsubmission1/final_5fold_sed_dense121_nomix_fold1_checkpoint_44_score0.7645.pt\",\n        \"clip_threshold\": 0.3,\n        \"threshold\": 0.3\n    },\n    {\n        \"model_class\": PANNsDense121Att,\n        \"config\": {\n            \"sample_rate\": 32000,\n            \"window_size\": 1024,\n            \"hop_size\": 320,\n            \"mel_bins\": 64,\n            \"fmin\": 50,\n            \"fmax\": 14000,\n            \"classes_num\": 264,\n            \"apply_aug\": True,\n            \"top_db\": None\n        },\n        \"weights_path\": \"../input/birdsongdetectionfinalsubmission1/final_5fold_sed_dense121_nomix_fold2_checkpoint_50_score0.7737.pt\",\n        \"clip_threshold\": 0.3,\n        \"threshold\": 0.3\n    },\n    {\n        \"model_class\": PANNsDense121Att,\n        \"config\": {\n            \"sample_rate\": 32000,\n            \"window_size\": 1024,\n            \"hop_size\": 320,\n            \"mel_bins\": 64,\n            \"fmin\": 50,\n            \"fmax\": 14000,\n            \"classes_num\": 264,\n            \"apply_aug\": True,\n            \"top_db\": None\n        },\n        \"weights_path\": \"../input/birdsongdetectionfinalsubmission1/final_5fold_sed_dense121_nomix_fold3_checkpoint_48_score0.7746.pt\",\n        \"clip_threshold\": 0.3,\n        \"threshold\": 0.3\n    },\n    {\n        \"model_class\": PANNsDense121Att,\n        \"config\": {\n            \"sample_rate\": 32000,\n            \"window_size\": 1024,\n            \"hop_size\": 320,\n            \"mel_bins\": 64,\n            \"fmin\": 50,\n            \"fmax\": 14000,\n            \"classes_num\": 264,\n            \"apply_aug\": True,\n            \"top_db\": None\n        },\n        \"weights_path\": \"../input/birdsongdetectionfinalsubmission1/final_5fold_sed_dense121_nomix_fold4_checkpoint_50_score0.7728.pt\",\n        \"clip_threshold\": 0.3,\n        \"threshold\": 0.3\n    },\n    {\n        \"model_class\": PANNsDense121Att,\n        \"config\": {\n            \"sample_rate\": 32000,\n            \"window_size\": 1024,\n            \"hop_size\": 320,\n            \"mel_bins\": 64,\n            \"fmin\": 50,\n            \"fmax\": 14000,\n            \"classes_num\": 264,\n            \"apply_aug\": True,\n            \"top_db\": None\n        },\n        \"weights_path\": \"../input/birdsongdetectionfinalsubmission1/final_sed_dense121_mix_fold0_2_checkpoint_50_score0.6842.pt\",\n        \"clip_threshold\": 0.3,\n        \"threshold\": 0.3\n    },\n    {\n        \"model_class\": PANNsDense121Att,\n        \"config\": {\n            \"sample_rate\": 32000,\n            \"window_size\": 1024,\n            \"hop_size\": 320,\n            \"mel_bins\": 64,\n            \"fmin\": 50,\n            \"fmax\": 14000,\n            \"classes_num\": 264,\n            \"apply_aug\": True,\n            \"top_db\": None\n        },\n        \"weights_path\": \"../input/birdsongdetectionfinalsubmission1/final_sed_dense121_mix_fold1_2_checkpoint_50_score0.6629.pt\",\n        \"clip_threshold\": 0.3,\n        \"threshold\": 0.3\n    },\n    {\n        \"model_class\": PANNsDense121Att,\n        \"config\": {\n            \"sample_rate\": 32000,\n            \"window_size\": 1024,\n            \"hop_size\": 320,\n            \"mel_bins\": 64,\n            \"fmin\": 50,\n            \"fmax\": 14000,\n            \"classes_num\": 264,\n            \"apply_aug\": True,\n            \"top_db\": None\n        },\n        \"weights_path\": \"../input/birdsongdetectionfinalsubmission1/final_sed_dense121_mix_fold2_2_checkpoint_50_score0.6884.pt\",\n        \"clip_threshold\": 0.3,\n        \"threshold\": 0.3\n    },\n    {\n        \"model_class\": PANNsDense121Att,\n        \"config\": {\n            \"sample_rate\": 32000,\n            \"window_size\": 1024,\n            \"hop_size\": 320,\n            \"mel_bins\": 64,\n            \"fmin\": 50,\n            \"fmax\": 14000,\n            \"classes_num\": 264,\n            \"apply_aug\": True,\n            \"top_db\": None\n        },\n        \"weights_path\": \"../input/birdsongdetectionfinalsubmission1/final_sed_dense121_mix_fold3_2_checkpoint_50_score0.6870.pt\",\n        \"clip_threshold\": 0.3,\n        \"threshold\": 0.3\n    }\n]\nPERIOD = 30\nSR = 32000\nvote_lim = 4\nTTA = 10","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"for lm in list_of_models:\n    lm[\"model\"] = get_model(lm[\"model_class\"], lm[\"config\"], lm[\"weights_path\"])","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 4. Predictions","metadata":{}},{"cell_type":"code","source":"def prediction_for_clip(test_df: pd.DataFrame,\n                        clip: np.ndarray, \n                        model,\n                        threshold,\n                       clip_threshold):\n\n    audios = []\n    y = clip.astype(np.float32)\n    len_y = len(y)\n    start = 0\n    end = PERIOD * SR\n    while True:\n        y_batch = y[start:end].astype(np.float32)\n        if len(y_batch) != PERIOD * SR:\n            y_pad = np.zeros(PERIOD * SR, dtype=np.float32)\n            y_pad[:len(y_batch)] = y_batch\n            audios.append(y_pad)\n            break\n        start = end\n        end += PERIOD * SR\n        audios.append(y_batch)\n        \n    array = np.asarray(audios)\n    tensors = torch.from_numpy(array)\n    \n    model.eval()\n    estimated_event_list = []\n    global_time = 0.0\n    site = test_df[\"site\"].values[0]\n    audio_id = test_df[\"audio_id\"].values[0]\n    for image in tensors:\n        image = image.unsqueeze(0).unsqueeze(0)\n        image = image.expand(image.shape[0], TTA, image.shape[2])\n        image = image.to(device)\n        \n        with torch.no_grad():\n            prediction = model((image, None))\n            framewise_outputs = prediction[\"framewise_output\"].detach(\n                ).cpu().numpy()[0].mean(axis=0)\n            clipwise_outputs = prediction[\"clipwise_output\"].detach(\n                ).cpu().numpy()[0].mean(axis=0)\n                \n        thresholded = framewise_outputs >= threshold\n        \n        clip_thresholded = clipwise_outputs >= clip_threshold\n        clip_indices = np.argwhere(clip_thresholded).reshape(-1)\n        clip_codes = []\n        for ci in clip_indices:\n            clip_codes.append(INV_BIRD_CODE[ci])\n            \n        for target_idx in range(thresholded.shape[1]):\n            if thresholded[:, target_idx].mean() == 0:\n                pass\n            else:\n                detected = np.argwhere(thresholded[:, target_idx]).reshape(-1)\n                head_idx = 0\n                tail_idx = 0\n                while True:\n                    if (tail_idx + 1 == len(detected)) or (\n                            detected[tail_idx + 1] - \n                            detected[tail_idx] != 1):\n                        onset = 0.01 * detected[\n                            head_idx] + global_time\n                        offset = 0.01 * detected[\n                            tail_idx] + global_time\n                        onset_idx = detected[head_idx]\n                        offset_idx = detected[tail_idx]\n                        max_confidence = framewise_outputs[\n                            onset_idx:offset_idx, target_idx].max()\n                        mean_confidence = framewise_outputs[\n                            onset_idx:offset_idx, target_idx].mean()\n                        if INV_BIRD_CODE[target_idx] in clip_codes:\n                            estimated_event = {\n                                \"site\": site,\n                                \"audio_id\": audio_id,\n                                \"ebird_code\": INV_BIRD_CODE[target_idx],\n                                \"clip_codes\": clip_codes,\n                                \"onset\": onset,\n                                \"offset\": offset,\n                                \"max_confidence\": max_confidence,\n                                \"mean_confidence\": mean_confidence\n                            }\n                            estimated_event_list.append(estimated_event)\n                        head_idx = tail_idx + 1\n                        tail_idx = tail_idx + 1\n                        if head_idx >= len(detected):\n                            break\n                    else:\n                        tail_idx += 1\n        global_time += PERIOD\n        \n    prediction_df = pd.DataFrame(estimated_event_list)\n    return prediction_df\n\ndef prediction(test_df: pd.DataFrame,\n               test_audio: Path,\n               list_of_model_details):\n    unique_audio_id = test_df.audio_id.unique()\n\n    warnings.filterwarnings(\"ignore\")\n    prediction_dfs_dict = defaultdict(list)\n    for audio_id in progress_bar(unique_audio_id):\n        clip, _ = librosa.load(test_audio / (audio_id + \".mp3\"),\n                               sr=SR,\n                               mono=True,\n                               res_type=\"kaiser_fast\")\n        \n        test_df_for_audio_id = test_df.query(\n            f\"audio_id == '{audio_id}'\").reset_index(drop=True)\n        for i, model_details in enumerate(list_of_model_details):\n            prediction_df = prediction_for_clip(test_df_for_audio_id,\n                                                clip=clip,\n                                                model=model_details[\"model\"],\n                                                threshold=model_details[\"threshold\"],\n                                               clip_threshold=model_details[\"clip_threshold\"])\n\n            prediction_dfs_dict[i].append(prediction_df)\n    list_of_prediction_df = []\n    for key, prediction_dfs in prediction_dfs_dict.items():\n        prediction_df = pd.concat(prediction_dfs, axis=0, sort=False).reset_index(drop=True)\n        list_of_prediction_df.append(prediction_df)\n    return list_of_prediction_df","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"list_of_prediction_df = prediction(test_df=test,\n                           test_audio=TEST_AUDIO_DIR,\n                           list_of_model_details=list_of_models)","metadata":{"_kg_hide-output":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 5. Post Process","metadata":{}},{"cell_type":"markdown","source":"后处理指一般训练模型标签音频类型后，还要作后处理去改善预测结果，预测结果需要按场景作调整","metadata":{}},{"cell_type":"code","source":"def get_post_post_process_predictions(prediction_df):\n    labels = {}\n\n    for audio_id, sub_df in progress_bar(prediction_df.groupby(\"audio_id\")):\n        events = sub_df[[\"ebird_code\", \"onset\", \"offset\", \"max_confidence\", \"site\"]].values\n        n_events = len(events)\n\n        site = events[0][4]\n        for i in range(n_events):\n            event = events[i][0]\n            onset = events[i][1]\n            offset = events[i][2]\n            \n            start_section = int((onset // 5) * 5) + 5\n            end_section = int((offset // 5) * 5) + 5\n            cur_section = start_section\n\n            row_id = f\"{site}_{audio_id}_{start_section}\"\n            if labels.get(row_id) is not None:\n                labels[row_id].add(event)\n            else:\n                labels[row_id] = set()\n                labels[row_id].add(event)\n\n            while cur_section != end_section:\n                cur_section += 5\n                row_id = f\"{site}_{audio_id}_{cur_section}\"\n                if labels.get(row_id) is not None:\n                    labels[row_id].add(event)\n                else:\n                    labels[row_id] = set()\n                    labels[row_id].add(event)\n\n\n    for key in labels:\n        labels[key] = \" \".join(sorted(list(labels[key])))\n\n\n    row_ids = list(labels.keys())\n    birds = list(labels.values())\n    post_processed = pd.DataFrame({\n        \"row_id\": row_ids,\n        \"birds\": birds\n    })\n    return post_processed","metadata":{"_kg_hide-input":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"all_row_id = test[[\"row_id\"]]\nlist_of_submissions = []\nfor prediction_df in list_of_prediction_df:\n    post_processed = get_post_post_process_predictions(prediction_df)\n    submission = post_processed.fillna(\"nocall\")\n    submission = submission.set_index('row_id')\n    list_of_submissions.append(submission)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"list_all_of_row_ids = []\nfor sub_x in list_of_submissions:\n    list_all_of_row_ids+= list(sub_x.index.values)\nlist_all_of_row_ids = list(set(list_all_of_row_ids))","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 6. Ensemble","metadata":{}},{"cell_type":"code","source":"final_submission = []\nfor row_id in list_all_of_row_ids:\n    birds = []\n    for sub in list_of_submissions:\n        if row_id in sub.index:\n            birds.extend(sub.loc[row_id].birds.split(\" \"))\n    birds = [x for x in birds if \"nocall\" != x and \"\" != x]\n    count_birds = Counter(birds)\n    final_birds = []\n    for key, value in count_birds.items():\n        if value >= vote_lim:\n            final_birds.append(key)\n    if len(final_birds)>0:\n        row_data = {\n            \"row_id\": row_id,\n            \"birds\": \" \".join(sorted(final_birds))\n        }\n    else:\n        row_data = {\n            \"row_id\": row_id,\n            \"birds\": \"nocall\"\n        }\n    final_submission.append(row_data)\n    \nsite_3_data = defaultdict(list)\nfor row in final_submission:\n    if \"site_3\" in row[\"row_id\"]:\n        final_row_id = \"_\".join(row[\"row_id\"].split(\"_\")[0:-1])\n        birds = row[\"birds\"].split(\" \")\n        birds = [x for x in birds if \"nocall\" != x and \"\" != x]\n        site_3_data[final_row_id].extend(birds)\n        \nfor key, value in site_3_data.items():\n    count_birds = Counter(value)\n    final_birds = []\n    for k, v in count_birds.items():\n        if v >= vote_lim:\n            final_birds.append(k)\n    if len(final_birds)>0:\n        row_data = {\n            \"row_id\": key,\n            \"birds\": \" \".join(sorted(final_birds))\n        }\n    else:\n        row_data = {\n            \"row_id\": key,\n            \"birds\": \"nocall\"\n        }\n    final_submission.append(row_data)\n\nfinal_submission = pd.DataFrame(final_submission)\nfinal_submission = all_row_id.merge(final_submission, on=\"row_id\", how=\"left\")\nfinal_submission = final_submission.fillna(\"nocall\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"final_submission.to_csv(\"submission.csv\", index=False)\nfinal_submission.head(50)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}