{"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":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\n# for dirname, _, filenames in os.walk('/kaggle/input'):\n#     for filename in filenames:\n#         print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-12-25T15:11:20.920681Z","iopub.execute_input":"2022-12-25T15:11:20.921034Z","iopub.status.idle":"2022-12-25T15:11:20.926746Z","shell.execute_reply.started":"2022-12-25T15:11:20.921001Z","shell.execute_reply":"2022-12-25T15:11:20.925804Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"try:\n    import resnest\nexcept ModuleNotFoundError:\n    !pip install -q \"../input/resnest50-fast-package/resnest-0.0.6b20200701/resnest\"","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:18:55.091032Z","iopub.execute_input":"2022-12-25T14:18:55.091592Z","iopub.status.idle":"2022-12-25T14:18:55.100593Z","shell.execute_reply.started":"2022-12-25T14:18:55.091555Z","shell.execute_reply":"2022-12-25T14:18:55.099777Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 匯入一些會用到的模組\nimport numpy as np\nimport librosa as lb\nimport soundfile as sf\nimport pandas as pd\nimport cv2\nfrom pathlib import Path\nimport re\nimport seaborn as sns\nimport matplotlib.pyplot as plt\nfrom sklearn.utils import shuffle\n\nfrom PIL import Image\n\nimport torch\nfrom torch import nn\nfrom  torch.utils.data import Dataset, DataLoader\n\nfrom tqdm.notebook import tqdm\n\nimport time\nimport resnest\nfrom resnest.torch import resnest50","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:18:55.102521Z","iopub.execute_input":"2022-12-25T14:18:55.102754Z","iopub.status.idle":"2022-12-25T14:18:55.111841Z","shell.execute_reply.started":"2022-12-25T14:18:55.102731Z","shell.execute_reply":"2022-12-25T14:18:55.111093Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 設定程式中會用到的參數的預測值，或指定一些會用到的檔案路徑\nNUM_CLASSES = 397\nSR = 32000  # 音頻採樣率 32 kHz\nDURATION = 5  # 計算時間序列的的持續時間（以秒為單位） -> 讀取時長\nTHRESH = 0.1  # thresh: threshold minimum power for log spectrogram\n\nSPEC_SHAPE = (48, 128) # height x width\nFMIN=5\nFMAX=None\n\n\nDEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nprint(\"DEVICE:\", DEVICE)\n\nTEST_AUDIO_ROOT = Path(\"../input/birdclef-2021/test_soundscapes\")\nSAMPLE_SUB_PATH = \"../input/birdclef-2021/sample_submission.csv\"\nTARGET_PATH = None\n    \nif not len(list(TEST_AUDIO_ROOT.glob(\"*.ogg\"))):\n    TEST_AUDIO_ROOT = Path(\"../input/birdclef-2021/train_soundscapes\")\n    SAMPLE_SUB_PATH = None\n    # SAMPLE_SUB_PATH = \"../input/birdclef-2021/sample_submission.csv\"\n    TARGET_PATH = Path(\"../input/birdclef-2021/train_soundscape_labels.csv\")\n    \nTRAIN_AUDIO_ROOT = Path(\"../input/birdclef-2021/train_short_audio\")\nTRAIN_AUDIO_IMAGES_SAVE_ROOT = Path(\"audio_images\") # Where to save the mels images\nTRAIN_AUDIO_IMAGES_SAVE_ROOT.mkdir(exist_ok=True, parents=True)\nTRAIN_AUDIO_TO_IMAGES_SAVE_ROOT = Path(\"audio_to_images\") # Where to save the mels images\nTRAIN_AUDIO_TO_IMAGES_SAVE_ROOT.mkdir(exist_ok=True, parents=True)","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:18:55.113578Z","iopub.execute_input":"2022-12-25T14:18:55.114184Z","iopub.status.idle":"2022-12-25T14:18:55.128427Z","shell.execute_reply.started":"2022-12-25T14:18:55.114149Z","shell.execute_reply":"2022-12-25T14:18:55.127456Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 由 pandas 讀取 csv，並存在一個 dataframe 裡\ndf = pd.read_csv(TARGET_PATH)\ndf","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:18:55.130006Z","iopub.execute_input":"2022-12-25T14:18:55.130380Z","iopub.status.idle":"2022-12-25T14:18:55.152354Z","shell.execute_reply.started":"2022-12-25T14:18:55.130346Z","shell.execute_reply":"2022-12-25T14:18:55.151561Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 檢視資料的概要資訊\ndf.info()","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:18:55.153641Z","iopub.execute_input":"2022-12-25T14:18:55.153995Z","iopub.status.idle":"2022-12-25T14:18:55.165675Z","shell.execute_reply.started":"2022-12-25T14:18:55.153958Z","shell.execute_reply":"2022-12-25T14:18:55.164545Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 查看是否有欄位存在 NaN 值 (True 代表有 NaN 值存在)\ndf.isna().any()","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:18:55.166995Z","iopub.execute_input":"2022-12-25T14:18:55.167337Z","iopub.status.idle":"2022-12-25T14:18:55.175642Z","shell.execute_reply.started":"2022-12-25T14:18:55.167301Z","shell.execute_reply":"2022-12-25T14:18:55.174673Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sns.countplot(df['site'])\n# 提供的音檔中只有兩個地區的資料 COR 及 SSW","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:18:55.177044Z","iopub.execute_input":"2022-12-25T14:18:55.177377Z","iopub.status.idle":"2022-12-25T14:18:55.286656Z","shell.execute_reply.started":"2022-12-25T14:18:55.177342Z","shell.execute_reply":"2022-12-25T14:18:55.285670Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"https://blog.csdn.net/zzc15806/article/details/79603994\n- 參數介紹\n    - sr：採樣率、取樣率\n    - hop_length：幀移\n    - overlapping：連續幀之間的重疊部分\n    - n_fft：窗口大小、視窗大小\n    - n_mels ：產生的梅爾帶數\n    - fmin ：最低頻率（Hz）\n    - fmax：最高頻率（以Hz為單位）。如果為None，則使用fmax = sr / 2.0\n- 會返回y值\n    - y ：音頻時間序列","metadata":{}},{"cell_type":"code","source":"class MelSpecComputer:  # 梅爾頻譜圖，為頻譜特徵提取的一個方法\n    def __init__(self, sr, n_mels, fmin, fmax, **kwargs):\n        self.sr = sr\n        self.n_mels = n_mels\n        self.fmin = fmin\n        self.fmax = fmax\n        kwargs[\"n_fft\"] = kwargs.get(\"n_fft\", self.sr//10)\n        kwargs[\"hop_length\"] = kwargs.get(\"hop_length\", self.sr//(10*4))\n        self.kwargs = kwargs\n\n    def __call__(self, y):\n\n        melspec = lb.feature.melspectrogram(\n            y, sr=self.sr, n_mels=self.n_mels, fmin=self.fmin, fmax=self.fmax, **self.kwargs,\n        )\n\n        # 將功率譜(幅值平方)轉換為分貝(dB)單位\n        melspec = lb.power_to_db(melspec).astype(np.float32)  # astype()：對資料型別進行轉換\n        return melspec","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:18:55.289079Z","iopub.execute_input":"2022-12-25T14:18:55.289576Z","iopub.status.idle":"2022-12-25T14:18:55.299228Z","shell.execute_reply.started":"2022-12-25T14:18:55.289534Z","shell.execute_reply":"2022-12-25T14:18:55.298224Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# mono：單聲道\n# X 是一個數組\ndef mono_to_color(X, eps=1e-6, mean=None, std=None):\n    mean = mean or X.mean()\n    std = std or X.std()\n    X = (X - mean) / (std + eps)\n    \n    _min, _max = X.min(), X.max()\n\n    if (_max - _min) > eps:\n        V = np.clip(X, _min, _max)  # 將X數組中的數值限制在min、max中。e.g. 若<min則該數就會變成min\n        V = 255 * (V - _min) / (_max - _min)\n        V = V.astype(np.uint8)\n    else:\n        V = np.zeros_like(X, dtype=np.uint8)  # 返回與指定數組具有相同形狀和數據類型的數組，並且數組中的值都為0。\n\n    return V\n\ndef crop_or_pad(y, length):\n    if len(y) < length:\n        y = np.concatenate([y, length - np.zeros(len(y))])\n    elif len(y) > length:\n        y = y[:length]\n    return y","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:18:55.301692Z","iopub.execute_input":"2022-12-25T14:18:55.302070Z","iopub.status.idle":"2022-12-25T14:18:55.318556Z","shell.execute_reply.started":"2022-12-25T14:18:55.302030Z","shell.execute_reply":"2022-12-25T14:18:55.317401Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"class BirdCLEFDataset(Dataset):\n    def __init__(self, data, sr=SR, n_mels=128, fmin=5, fmax=None, duration=DURATION, step=None, res_type=\"kaiser_fast\", resample=True):\n        \n        self.data = data\n        \n        self.sr = sr\n        self.n_mels = n_mels\n        self.fmin = fmin\n        self.fmax = fmax or self.sr//2\n\n        self.duration = duration\n        self.audio_length = self.duration*self.sr\n        self.step = step or self.audio_length\n        \n        self.res_type = res_type\n        self.resample = resample\n\n        self.mel_spec_computer = MelSpecComputer(sr=self.sr, n_mels=self.n_mels, fmin=self.fmin, fmax=self.fmax)\n    \n    def __len__(self):\n        return len(self.data)\n    \n    @staticmethod\n    def normalize(image):\n        image = image.astype(\"float32\", copy=False) / 255.0\n        image = np.stack([image, image, image])\n        return image\n    \n    def audio_to_image(self, audio):\n        melspec = self.mel_spec_computer(audio) \n        image = mono_to_color(melspec)\n        image = self.normalize(image)\n        return image\n\n    def read_file(self, filepath):\n        audio, orig_sr = sf.read(filepath, dtype=\"float32\")  # 讀取soundfile\n\n        if self.resample and orig_sr != self.sr:  # 重取樣\n            audio = lb.resample(audio, orig_sr, self.sr, res_type=self.res_type)\n          \n        audios = []\n        for i in range(self.audio_length, len(audio) + self.step, self.step):\n            start = max(0, i - self.audio_length)\n            end = start + self.audio_length\n            audios.append(audio[start:end])\n            \n        if len(audios[-1]) < self.audio_length:\n            audios = audios[:-1]\n            \n        images = [self.audio_to_image(audio) for audio in audios]\n        images = np.stack(images)\n        \n        return images\n    \n        \n    def __getitem__(self, idx):\n        return self.read_file(self.data.loc[idx, \"filepath\"])","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:18:55.320916Z","iopub.execute_input":"2022-12-25T14:18:55.321561Z","iopub.status.idle":"2022-12-25T14:18:55.348463Z","shell.execute_reply.started":"2022-12-25T14:18:55.321495Z","shell.execute_reply":"2022-12-25T14:18:55.346418Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 讀取 train_metadata.csv 並取出評分高的錄音檔","metadata":{}},{"cell_type":"code","source":"train = pd.read_csv('../input/birdclef-2021/train_metadata.csv',)\n\n# only use high quality samples\ntrain = train.query('rating>=4')\n\nbirds_count = {}\nfor bird_species, count in zip(train.primary_label.unique(), \n                               train.groupby('primary_label')['primary_label'].count().values):\n    birds_count[bird_species] = count\n\nmost_represented_birds = [key for key,value in birds_count.items() if value >= 200] \n\nTRAIN = train.query('primary_label in @most_represented_birds')","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:18:55.352395Z","iopub.execute_input":"2022-12-25T14:18:55.352715Z","iopub.status.idle":"2022-12-25T14:18:55.691117Z","shell.execute_reply.started":"2022-12-25T14:18:55.352674Z","shell.execute_reply":"2022-12-25T14:18:55.690209Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 將音訊轉成梅爾頻譜圖","metadata":{}},{"cell_type":"code","source":"def get_spectrograms(filepath, primary_label, output_dir):\n    \n    # Open the file with lb (limited to the first 15 seconds)\n    sig, rate = lb.load(filepath, sr=SR, offset=None, duration=15)\n    \n    # Split DURATION into five second chunks\n    sig_splits = []\n    for i in range(0, len(sig), int(DURATION * SR)):\n        split = sig[i:i + int(DURATION * SR)]\n\n        # End of DURATION?\n        if len(split) < int(DURATION * SR):\n            break\n        \n        sig_splits.append(split)\n        \n    # Extract mel spectrograms for each audio chunk\n    s_cnt = 0\n    saved_samples = []\n    for chunk in sig_splits:\n        \n        hop_length = int(DURATION * SR / (SPEC_SHAPE[1] - 1))\n        mel_spec = lb.feature.melspectrogram(y=chunk, \n                                                  sr=SR, \n                                                  n_fft=1024, \n                                                  hop_length=hop_length, \n                                                  n_mels=SPEC_SHAPE[0], \n                                                  fmin=FMIN, \n                                                  fmax=FMAX)\n    \n        mel_spec = lb.power_to_db(mel_spec, ref=np.max)  # 將功率譜(幅值平方)轉換為dB單位\n        \n        # Normalize\n        mel_spec -= mel_spec.min()\n        mel_spec /= mel_spec.max()\n        \n        # Save as image file\n        save_dir = os.path.join(output_dir, primary_label)\n        if not os.path.exists(save_dir):\n            os.makedirs(save_dir)\n        save_path = os.path.join(save_dir, filepath.rsplit(os.sep, 1)[-1].rsplit('.', 1)[0] + '_' + str(s_cnt) + '.png')\n        im = Image.fromarray(mel_spec * 255.0).convert(\"L\")\n        im.save(save_path)\n        \n        saved_samples.append(save_path)\n        s_cnt += 1\n        \n        \n    return saved_samples\n\nprint('FINAL NUMBER OF AUDIO FILES IN TRAINING DATA:', len(TRAIN))","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:18:55.692596Z","iopub.execute_input":"2022-12-25T14:18:55.693090Z","iopub.status.idle":"2022-12-25T14:18:55.706338Z","shell.execute_reply.started":"2022-12-25T14:18:55.693055Z","shell.execute_reply":"2022-12-25T14:18:55.705392Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"samples = []\nwith tqdm(total=len(TRAIN)) as pbar:\n    for idx, row in TRAIN.iterrows():\n        pbar.update(1)\n        \n        if row.primary_label in most_represented_birds:\n            audio_file_path = os.path.join(TRAIN_AUDIO_ROOT, row.primary_label, row.filename)\n            samples += get_spectrograms(audio_file_path, row.primary_label, TRAIN_AUDIO_IMAGES_SAVE_ROOT)\n\nTRAIN_SPECS = shuffle(samples, random_state=1337)","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:18:55.707742Z","iopub.execute_input":"2022-12-25T14:18:55.708311Z","iopub.status.idle":"2022-12-25T14:28:08.127022Z","shell.execute_reply.started":"2022-12-25T14:18:55.708277Z","shell.execute_reply":"2022-12-25T14:28:08.125699Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Plot the first 12 spectrograms of TRAIN_SPECS\nplt.figure(figsize=(60, 28))\nfor i in range(32):\n    spec = Image.open(TRAIN_SPECS[i])\n    plt.subplot(8, 4, i + 1)\n    plt.title(TRAIN_SPECS[i].split(os.sep)[-1])\n    plt.imshow(spec, origin='lower')","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:28:08.128463Z","iopub.execute_input":"2022-12-25T14:28:08.128798Z","iopub.status.idle":"2022-12-25T14:28:11.716496Z","shell.execute_reply.started":"2022-12-25T14:28:08.128758Z","shell.execute_reply":"2022-12-25T14:28:11.715675Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"list(TEST_AUDIO_ROOT.glob(\"*.ogg\"))","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:28:11.717711Z","iopub.execute_input":"2022-12-25T14:28:11.718198Z","iopub.status.idle":"2022-12-25T14:28:11.727161Z","shell.execute_reply.started":"2022-12-25T14:28:11.718162Z","shell.execute_reply":"2022-12-25T14:28:11.725750Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"TEST_AUDIO_ROOT","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:28:11.728683Z","iopub.execute_input":"2022-12-25T14:28:11.729161Z","iopub.status.idle":"2022-12-25T14:28:11.734968Z","shell.execute_reply.started":"2022-12-25T14:28:11.729121Z","shell.execute_reply":"2022-12-25T14:28:11.734051Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"data = pd.DataFrame(\n     [(path.stem, *path.stem.split(\"_\"), path) for path in Path(TEST_AUDIO_ROOT).glob(\"*.ogg\")],\n    columns = [\"filename\", \"id\", \"site\", \"date\", \"filepath\"]\n)\nprint(data.shape)\ndata.head(20)","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:28:11.736562Z","iopub.execute_input":"2022-12-25T14:28:11.736984Z","iopub.status.idle":"2022-12-25T14:28:11.762943Z","shell.execute_reply.started":"2022-12-25T14:28:11.736950Z","shell.execute_reply":"2022-12-25T14:28:11.762061Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train = pd.read_csv(\"../input/birdclef-2021/train_metadata.csv\")\n\n# print(len(df_train[\"primary_label\"]))  # train_metadata內總共有62874筆資料\n# print(len(df_train[\"primary_label\"].unique()))  # 有397種label\n\nLABEL_IDS = {label: label_id for label_id,label in enumerate(sorted(df_train[\"primary_label\"].unique()))}  # 將種類加上id 變成dict\nINV_LABEL_IDS = {val: key for key,val in LABEL_IDS.items()}  # dict 倒轉 key val 位置交換 -> {label_id: 'label'}","metadata":{"_kg_hide-input":false,"execution":{"iopub.status.busy":"2022-12-25T14:28:11.764229Z","iopub.execute_input":"2022-12-25T14:28:11.764747Z","iopub.status.idle":"2022-12-25T14:28:11.992192Z","shell.execute_reply.started":"2022-12-25T14:28:11.764706Z","shell.execute_reply":"2022-12-25T14:28:11.991312Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train.head(5)","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:28:11.994454Z","iopub.execute_input":"2022-12-25T14:28:11.994809Z","iopub.status.idle":"2022-12-25T14:28:12.016345Z","shell.execute_reply.started":"2022-12-25T14:28:11.994762Z","shell.execute_reply":"2022-12-25T14:28:12.015315Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train.info()","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:28:12.020382Z","iopub.execute_input":"2022-12-25T14:28:12.020647Z","iopub.status.idle":"2022-12-25T14:28:12.055465Z","shell.execute_reply.started":"2022-12-25T14:28:12.020620Z","shell.execute_reply":"2022-12-25T14:28:12.054503Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_train.isna().any()","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:28:12.057534Z","iopub.execute_input":"2022-12-25T14:28:12.058144Z","iopub.status.idle":"2022-12-25T14:28:12.089976Z","shell.execute_reply.started":"2022-12-25T14:28:12.058096Z","shell.execute_reply":"2022-12-25T14:28:12.088927Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# 在給定的數據集中(網友給的評價)，可以看出評價都偏好\nsns.countplot(df_train['rating'])","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:28:12.091158Z","iopub.execute_input":"2022-12-25T14:28:12.091674Z","iopub.status.idle":"2022-12-25T14:28:12.264841Z","shell.execute_reply.started":"2022-12-25T14:28:12.091624Z","shell.execute_reply":"2022-12-25T14:28:12.263975Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_data = BirdCLEFDataset(data=data)\nlen(test_data), test_data[0].shape","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:28:12.266174Z","iopub.execute_input":"2022-12-25T14:28:12.266665Z","iopub.status.idle":"2022-12-25T14:28:15.752475Z","shell.execute_reply.started":"2022-12-25T14:28:12.266626Z","shell.execute_reply":"2022-12-25T14:28:15.751537Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_net(checkpoint_path, num_classes=NUM_CLASSES):\n    net = resnest50(pretrained=False)\n    net.fc = nn.Linear(net.fc.in_features, num_classes)\n    dummy_device = torch.device(\"cpu\")\n    d = torch.load(checkpoint_path, map_location=dummy_device)\n    for key in list(d.keys()):\n        d[key.replace(\"model.\", \"\")] = d.pop(key)\n    net.load_state_dict(d)\n    net = net.to(DEVICE)\n    net = net.eval()\n    return net","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:28:15.753559Z","iopub.execute_input":"2022-12-25T14:28:15.753896Z","iopub.status.idle":"2022-12-25T14:28:15.765920Z","shell.execute_reply.started":"2022-12-25T14:28:15.753850Z","shell.execute_reply":"2022-12-25T14:28:15.764748Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 將CNN深度學習模型匯入","metadata":{}},{"cell_type":"code","source":"checkpoint_paths = [\n    Path(\"../input/kkiller-birdclef-models-public/birdclef_resnest50_fold0_epoch_10_f1_val_06471_20210417161101.pth\"),\n]\n\n\nnets = [\n        load_net(checkpoint_path.as_posix()) for checkpoint_path in checkpoint_paths\n]\n\nprint(nets)","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:28:15.767311Z","iopub.execute_input":"2022-12-25T14:28:15.767643Z","iopub.status.idle":"2022-12-25T14:28:16.512395Z","shell.execute_reply.started":"2022-12-25T14:28:15.767609Z","shell.execute_reply":"2022-12-25T14:28:16.511522Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"@torch.no_grad()\ndef get_thresh_preds(out, thresh=None):\n    thresh = thresh or THRESH\n    o = (-out).argsort(1)\n    npreds = (out > thresh).sum(1)\n    preds = []\n    for oo, npred in zip(o, npreds):\n        preds.append(oo[:npred].cpu().numpy().tolist())\n    return preds","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:28:16.513630Z","iopub.execute_input":"2022-12-25T14:28:16.514145Z","iopub.status.idle":"2022-12-25T14:28:16.520398Z","shell.execute_reply.started":"2022-12-25T14:28:16.514105Z","shell.execute_reply":"2022-12-25T14:28:16.519466Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_bird_names(preds):\n    bird_names = []\n    for pred in preds:\n        if not pred:\n            bird_names.append(\"nocall\")\n        else:\n            bird_names.append(\" \".join([INV_LABEL_IDS[bird_id] for bird_id in pred]))\n    return bird_names","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:28:16.521567Z","iopub.execute_input":"2022-12-25T14:28:16.522008Z","iopub.status.idle":"2022-12-25T14:28:16.531805Z","shell.execute_reply.started":"2022-12-25T14:28:16.521958Z","shell.execute_reply":"2022-12-25T14:28:16.530923Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def predict(nets, test_data, names=True):\n    preds = []\n    with torch.no_grad():\n        for idx in  tqdm(list(range(len(test_data)))):\n            xb = torch.from_numpy(test_data[idx]).to(DEVICE)\n            pred = 0.\n            for net in nets:\n                o = net(xb)\n                o = torch.sigmoid(o)\n\n                pred += o\n\n            pred /= len(nets)\n            \n            if names:\n                pred = get_bird_names(get_thresh_preds(pred))\n\n            preds.append(pred)\n    return preds","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:28:16.533117Z","iopub.execute_input":"2022-12-25T14:28:16.533517Z","iopub.status.idle":"2022-12-25T14:28:16.541694Z","shell.execute_reply.started":"2022-12-25T14:28:16.533482Z","shell.execute_reply":"2022-12-25T14:28:16.540853Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## 將測試數據丟入演算法中預測","metadata":{}},{"cell_type":"code","source":"pred_probas = predict(nets, test_data, names=False)\nprint(len(pred_probas))","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:28:16.542690Z","iopub.execute_input":"2022-12-25T14:28:16.545015Z","iopub.status.idle":"2022-12-25T14:29:25.629718Z","shell.execute_reply.started":"2022-12-25T14:28:16.544979Z","shell.execute_reply":"2022-12-25T14:29:25.628542Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"preds = [get_bird_names(get_thresh_preds(pred, thresh=THRESH)) for pred in pred_probas]","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:29:25.631145Z","iopub.execute_input":"2022-12-25T14:29:25.631518Z","iopub.status.idle":"2022-12-25T14:29:25.781592Z","shell.execute_reply.started":"2022-12-25T14:29:25.631479Z","shell.execute_reply":"2022-12-25T14:29:25.780685Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def preds_as_df(data, preds):\n    sub = {\n        \"row_id\": [],\n        \"birds\": [],\n    }\n    \n    for row, pred in zip(data.itertuples(False), preds):\n        row_id = [f\"{row.id}_{row.site}_{5*i}\" for i in range(1, len(pred)+1)]\n        sub[\"birds\"] += pred\n        sub[\"row_id\"] += row_id\n        \n    sub = pd.DataFrame(sub)\n    \n    if SAMPLE_SUB_PATH:\n        sample_sub = pd.read_csv(SAMPLE_SUB_PATH, usecols=[\"row_id\"])\n        sub = sample_sub.merge(sub, on=\"row_id\", how=\"left\")\n        sub[\"birds\"] = sub[\"birds\"].fillna(\"nocall\")\n    return sub","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:29:25.782936Z","iopub.execute_input":"2022-12-25T14:29:25.783255Z","iopub.status.idle":"2022-12-25T14:29:25.791710Z","shell.execute_reply.started":"2022-12-25T14:29:25.783220Z","shell.execute_reply":"2022-12-25T14:29:25.790814Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"每段音頻將以5秒為一單位分割去做分類預測\n\n故一筆十分鐘的音訊會有120筆資料，總共會產生120*20=2400行數據","metadata":{}},{"cell_type":"code","source":"sub = preds_as_df(data, preds)\nprint(sub.shape)\nsub.head(100)","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:29:25.793101Z","iopub.execute_input":"2022-12-25T14:29:25.793669Z","iopub.status.idle":"2022-12-25T14:29:25.812866Z","shell.execute_reply.started":"2022-12-25T14:29:25.793627Z","shell.execute_reply":"2022-12-25T14:29:25.811978Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sub.to_csv(\"submission.csv\", index=False)","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:29:25.814213Z","iopub.execute_input":"2022-12-25T14:29:25.814739Z","iopub.status.idle":"2022-12-25T14:29:25.827289Z","shell.execute_reply.started":"2022-12-25T14:29:25.814700Z","shell.execute_reply":"2022-12-25T14:29:25.826482Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# 模組評分","metadata":{}},{"cell_type":"code","source":"def get_metrics(s_true, s_pred):\n    s_true = set(s_true.split())\n    s_pred = set(s_pred.split())\n    n, n_true, n_pred = len(s_true.intersection(s_pred)), len(s_true), len(s_pred)\n    \n    prec = n/n_pred\n    rec = n/n_true\n    f1 = 2*prec*rec/(prec + rec) if prec + rec else 0\n    \n    return {\"f1\": f1, \"prec\": prec, \"rec\": rec, \"n_true\": n_true, \"n_pred\": n_pred, \"n\": n}","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:29:25.830240Z","iopub.execute_input":"2022-12-25T14:29:25.830483Z","iopub.status.idle":"2022-12-25T14:29:25.837638Z","shell.execute_reply.started":"2022-12-25T14:29:25.830458Z","shell.execute_reply":"2022-12-25T14:29:25.836775Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"if TARGET_PATH:\n    sub_target = pd.read_csv(TARGET_PATH)\n    sub_target = sub_target.merge(sub, how=\"left\", on=\"row_id\")\n    \n    print(sub_target[\"birds_x\"].notnull().sum(), sub_target[\"birds_x\"].notnull().sum())\n    assert sub_target[\"birds_x\"].notnull().all()\n    assert sub_target[\"birds_y\"].notnull().all()\n    \n    df_metrics = pd.DataFrame([get_metrics(s_true, s_pred) for s_true, s_pred in zip(sub_target.birds_x, sub_target.birds_y)])\n    \n    print(df_metrics.mean())","metadata":{"execution":{"iopub.status.busy":"2022-12-25T14:29:25.839851Z","iopub.execute_input":"2022-12-25T14:29:25.840429Z","iopub.status.idle":"2022-12-25T14:29:25.872372Z","shell.execute_reply.started":"2022-12-25T14:29:25.840393Z","shell.execute_reply":"2022-12-25T14:29:25.871494Z"},"trusted":true},"execution_count":null,"outputs":[]}]}