{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.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":70203,"databundleVersionId":8068726,"sourceType":"competition"},{"sourceId":172204890,"sourceType":"kernelVersion"}],"dockerImageVersionId":30683,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nfrom pathlib import Path\nimport torch\nimport matplotlib.pyplot as plt\nimport numpy as np\nfrom tqdm import tqdm\nimport plotly.express as px","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-04-16T01:06:23.235953Z","iopub.execute_input":"2024-04-16T01:06:23.236401Z","iopub.status.idle":"2024-04-16T01:06:27.947337Z","shell.execute_reply.started":"2024-04-16T01:06:23.236363Z","shell.execute_reply":"2024-04-16T01:06:27.946529Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df = pd.read_csv('/kaggle/input/birdclef-2024/train_metadata.csv')\n\nDEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'\nDEVICE","metadata":{"execution":{"iopub.status.busy":"2024-04-16T01:06:27.948890Z","iopub.execute_input":"2024-04-16T01:06:27.949332Z","iopub.status.idle":"2024-04-16T01:06:28.295196Z","shell.execute_reply.started":"2024-04-16T01:06:27.949303Z","shell.execute_reply":"2024-04-16T01:06:28.294310Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Fetch precomputed embeddings from Google Bird Vocalization Classifier\n# combine first and last embedding of all recordings (corresponds to first and last 5 seconds of audio)\nembeddings = torch.load('/kaggle/input/bc24-google-bird-model-embeddings-predict-score/embeddings.pt')\nfirst5sec_embeddings = torch.stack([torch.tensor(embeddings[filename][0]) for filename in df.filename])\nlast5sec_embeddings = torch.stack([torch.tensor(embeddings[filename][-1]) for filename in df.filename])\ncombined_embeddings = torch.concat([first5sec_embeddings, last5sec_embeddings], -1)\ncombined_embeddings.shape","metadata":{"execution":{"iopub.status.busy":"2024-04-16T01:06:28.296440Z","iopub.execute_input":"2024-04-16T01:06:28.296848Z","iopub.status.idle":"2024-04-16T01:07:19.119971Z","shell.execute_reply.started":"2024-04-16T01:06:28.296779Z","shell.execute_reply":"2024-04-16T01:07:19.118912Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# compute euclidean distances between each cell of a 2d matrix\ndef get_distances(x):\n    result = []\n    # loop over each row, since doing it all at once would cause a memory error\n    for row in tqdm(x):\n        result.append(((row[:, None] - x.T) ** 2).T.sum(-1))\n    return torch.stack(result)\n\ndistances = get_distances(combined_embeddings.to(DEVICE))\n\n# save distances so it can be analyzed in other notebooks\nnp.save('distances.npy', distances.to('cpu').numpy())","metadata":{"execution":{"iopub.status.busy":"2024-04-16T01:07:19.122437Z","iopub.execute_input":"2024-04-16T01:07:19.122962Z","iopub.status.idle":"2024-04-16T01:08:19.291351Z","shell.execute_reply.started":"2024-04-16T01:07:19.122923Z","shell.execute_reply":"2024-04-16T01:08:19.290543Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plt.figure(figsize=(9, 9))\nplt.title('All embedding distances (notice 0 along the diagonal, as expected)')\nplt.imshow(distances.to('cpu'))\nplt.colorbar();","metadata":{"execution":{"iopub.status.busy":"2024-04-16T01:09:01.819635Z","iopub.execute_input":"2024-04-16T01:09:01.820276Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# ignore diagonal & half of matrix, since it's symetrical\nnot_eye = (1 - torch.triu(torch.ones(distances.shape)).to(DEVICE)).bool()\n\ndef get_counts_for_threshold_range(start, end, resolution):\n    counts = []\n    thresholds = np.linspace(start, end, resolution)\n    for threshold in tqdm(thresholds):\n        counts.append(((distances < threshold) & not_eye).sum())\n    return thresholds, torch.stack(counts)\n\n\n# plot all pair distances\nthresholds, counts = get_counts_for_threshold_range(0, distances.max().item(), 1000)\npx.line(\n    x=thresholds,\n    y=counts.to('cpu'),\n    title=\"Distribution of all distances\",\n    labels={\"y\": \"number of recording pairs beneath threshold\", \"x\": \"embedding distnace threshold\"}\n)","metadata":{"execution":{"iopub.status.busy":"2024-04-16T01:09:54.064460Z","iopub.execute_input":"2024-04-16T01:09:54.065079Z","iopub.status.idle":"2024-04-16T01:10:27.128431Z","shell.execute_reply.started":"2024-04-16T01:09:54.065048Z","shell.execute_reply":"2024-04-16T01:10:27.127565Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# plot low pair distances\nthresholds, counts = get_counts_for_threshold_range(0, 5, 1000)\npx.line(\n    x=thresholds,\n    y=counts.to('cpu'),\n    title=\"Distribution of all distances\",\n    labels={\"y\": \"number of recording pairs beneath threshold\", \"x\": \"embedding distnace threshold\"}\n)","metadata":{"execution":{"iopub.status.busy":"2024-04-16T01:11:40.973675Z","iopub.execute_input":"2024-04-16T01:11:40.974029Z","iopub.status.idle":"2024-04-16T01:12:08.700424Z","shell.execute_reply.started":"2024-04-16T01:11:40.974000Z","shell.execute_reply":"2024-04-16T01:12:08.699567Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}