{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":21669,"databundleVersionId":1692278,"sourceType":"competition"}],"dockerImageVersionId":31090,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## Import","metadata":{}},{"cell_type":"code","source":"#Data handling\nimport os\nimport pandas as pd\nimport itertools\nfrom PIL import Image\n\n# Randomization\nimport random\n\n# Audio handling\n!pip install PySoundFile\nimport librosa\nfrom IPython.display import Audio\n\n# Visualization\nimport matplotlib.pyplot as plt\nfrom sklearn.manifold import TSNE\n\n# Feedback with progress bar\nfrom tqdm.notebook import tqdm\n\n# Math & Algorithms\nimport numpy as np\n\n# Model\nimport keras\nfrom keras import layers\nfrom tensorflow.keras import models, layers\n\n# Clustering\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.cluster import KMeans","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:31:37.742297Z","iopub.execute_input":"2025-10-28T13:31:37.742592Z","iopub.status.idle":"2025-10-28T13:31:40.941012Z","shell.execute_reply.started":"2025-10-28T13:31:37.742570Z","shell.execute_reply":"2025-10-28T13:31:40.940029Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Variable setting","metadata":{}},{"cell_type":"code","source":"# Initialize random number generation\nrandom_seed = 42\nrandom.seed(random_seed)\nrng = np.random.default_rng()\n\n# NN training parameters\ntarget_shape=(256, 512)\nlatent_dim=256\nbatch_size = 32\nlr = 1e-3\npatience = 10\nepochs = 50\nnum_files=1000 # number of files used for training\nnum_clusters=24\n\n# Folder for storing generated spectrograms\nsave_path='/kaggle/working/spectrograms'\nos.makedirs(save_path, exist_ok=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:31:40.943267Z","iopub.execute_input":"2025-10-28T13:31:40.943534Z","iopub.status.idle":"2025-10-28T13:31:40.949808Z","shell.execute_reply.started":"2025-10-28T13:31:40.943510Z","shell.execute_reply":"2025-10-28T13:31:40.949178Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## File path","metadata":{}},{"cell_type":"code","source":"# Root data path for RainForest Species\ninput_path='/kaggle/input/rfcx-species-audio-detection'\n\n# Train and Test audio recordings data\ntrain_path=os.path.join(input_path, 'train')\ntest_path=os.path.join(input_path, 'test')\n\n# Labels\ntp_label_csv_path=os.path.join(input_path, 'train_tp.csv')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:31:40.950612Z","iopub.execute_input":"2025-10-28T13:31:40.950900Z","iopub.status.idle":"2025-10-28T13:31:40.961774Z","shell.execute_reply.started":"2025-10-28T13:31:40.950875Z","shell.execute_reply":"2025-10-28T13:31:40.960818Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Number of files\nnum_train_files=len([f for f in os.listdir(train_path) if os.path.isfile(os.path.join(train_path, f))])\nnum_test_files=len([f for f in os.listdir(test_path) if os.path.isfile(os.path.join(test_path, f))])\nnum_tp_rows=len(pd.read_csv(tp_label_csv_path))\nprint(f\"Number of training files: {num_train_files}\")\nprint(f\"Number of test files: {num_test_files}\")\nprint(f\"Number of labeled parts (true positive): {num_tp_rows}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:31:40.963527Z","iopub.execute_input":"2025-10-28T13:31:40.964031Z","iopub.status.idle":"2025-10-28T13:31:59.522330Z","shell.execute_reply.started":"2025-10-28T13:31:40.964007Z","shell.execute_reply":"2025-10-28T13:31:59.521532Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Chooses files randomly from the given folder\ndef random_files(source_path, num_files=1):\n\n    all_files=os.listdir(source_path)\n    chosen_files=random.sample(all_files, num_files)\n    return [os.path.join(source_path, f) for f in chosen_files]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:31:59.523380Z","iopub.execute_input":"2025-10-28T13:31:59.523662Z","iopub.status.idle":"2025-10-28T13:31:59.527877Z","shell.execute_reply.started":"2025-10-28T13:31:59.523631Z","shell.execute_reply":"2025-10-28T13:31:59.527055Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"random_file, sr=librosa.core.load(random_files(train_path)[0])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:31:59.528654Z","iopub.execute_input":"2025-10-28T13:31:59.528923Z","iopub.status.idle":"2025-10-28T13:31:59.631981Z","shell.execute_reply.started":"2025-10-28T13:31:59.528901Z","shell.execute_reply":"2025-10-28T13:31:59.631398Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Data generation","metadata":{}},{"cell_type":"code","source":"# Generates spectrogram from the given file\ndef spec_gen(file_path, target_shape=(128, 256), sr=22050):\n    \n    audio, sr=librosa.core.load(file_path)\n    S=librosa.feature.melspectrogram(y=audio, sr=sr, n_mels=target_shape[0]) # f_max, f_min \n    S_db=librosa.power_to_db(S, ref=np.max)\n\n    if S_db.shape[1]>target_shape[1]:\n        S_db=S_db[:, :target_shape[1]]\n    elif S_db.shape[1]<target_shape[1]:\n        pad_width=[(0, 0), (0, target_shape[1]-S_db.shape[1])]\n        S_db=np.pad(S_db, pad_width=pad_width, mode='constant')\n    \n    S_norm = (S_db - S_db.min()) / (S_db.max() - S_db.min())\n    \n    return S_norm","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:31:59.632674Z","iopub.execute_input":"2025-10-28T13:31:59.632942Z","iopub.status.idle":"2025-10-28T13:31:59.638924Z","shell.execute_reply.started":"2025-10-28T13:31:59.632924Z","shell.execute_reply":"2025-10-28T13:31:59.638171Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Generates and saves spectrograms from a given number of files\ndef spec_save(source_path, save_path, target_shape=(128, 256), num_files=100, sr=22050):\n    \n    files=[f for f in os.scandir(source_path) if f.is_file()]\n    files=files[:num_files]\n    for f in tqdm(files):\n        if f.is_file():\n            output=os.path.join(save_path, os.path.splitext(f.name)[0]+\".png\")\n            spec=spec_gen(f.path, target_shape, sr)\n            spec=(spec*255).astype(np.uint8)\n            spec=Image.fromarray(spec, mode='L')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:31:59.639578Z","iopub.execute_input":"2025-10-28T13:31:59.639825Z","iopub.status.idle":"2025-10-28T13:31:59.648821Z","shell.execute_reply.started":"2025-10-28T13:31:59.639803Z","shell.execute_reply":"2025-10-28T13:31:59.648136Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"spec_save(train_path, save_path, target_shape, num_files=num_files, sr=sr)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:31:59.649530Z","iopub.execute_input":"2025-10-28T13:31:59.650332Z","iopub.status.idle":"2025-10-28T13:34:34.870498Z","shell.execute_reply.started":"2025-10-28T13:31:59.650309Z","shell.execute_reply":"2025-10-28T13:34:34.869540Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Autoencoder","metadata":{}},{"cell_type":"code","source":"def conv_autoencoder(input_shape=(128, 256, 1), latent_dim=64):\n\n    # Encoder\n    encoder_input=layers.Input(shape=input_shape)\n    x=layers.Conv2D(16, (3,3), activation='relu', padding='same')(encoder_input)\n    x=layers.MaxPooling2D((2,2), padding='same')(x)\n    x=layers.Conv2D(32, (3,3), activation='relu', padding='same')(x)\n    x=layers.MaxPooling2D((2,2), padding='same')(x)\n    x=layers.Conv2D(64, (3,3), activation='relu', padding='same')(x)\n    x=layers.MaxPooling2D((2,2), padding='same')(x)\n    # x=layers.Conv2D(128, (3,3), activation='relu', padding='same')(x)\n    # x=layers.MaxPooling2D((2,2), padding='same')(x)\n    x_shape=x.shape[1:]\n    x_prod=np.prod(x_shape)\n\n    # Bottleneck\n    x=layers.Flatten()(x)\n    latent=layers.Dense(latent_dim, activation='relu')(x)\n    encoder=models.Model(encoder_input, latent)\n\n    # Decoder\n    decoder_input=layers.Input(shape=(latent_dim,))\n    x=layers.Dense(x_prod, activation='relu')(decoder_input)\n    x=layers.Reshape((x_shape))(x)\n\n    # x = layers.Conv2DTranspose(128, (3,3), strides=(2,2), activation='relu', padding='same')(x)\n    x = layers.Conv2DTranspose(64, (3,3), strides=(2,2), activation='relu', padding='same')(x)\n    x = layers.Conv2DTranspose(32, (3,3), strides=(2,2), activation='relu', padding='same')(x)\n    x = layers.Conv2DTranspose(16, (3,3), strides=(2,2), activation='relu', padding='same')(x)\n    \n    # x=layers.Conv2D(128, (3,3), activation='relu', padding='same')(x)\n    # x=layers.UpSampling2D((2, 2))(x)\n    # x=layers.Conv2D(64, (3,3), activation='relu', padding='same')(x)\n    # x=layers.UpSampling2D((2, 2))(x)\n    # x=layers.Conv2D(32, (3,3), activation='relu', padding='same')(x)\n    # x=layers.UpSampling2D((2, 2))(x)\n    # x=layers.Conv2D(16, (3,3), activation='relu', padding='same')(x)\n    # x=layers.UpSampling2D((2, 2))(x)\n    decoder_output=layers.Conv2D(1, (3, 3), activation='sigmoid', padding='same')(x)\n    decoder=models.Model(decoder_input, decoder_output)\n\n    # Autoencoder\n    autoencoder_output=decoder(encoder(encoder_input))\n    autoencoder=models.Model(encoder_input, autoencoder_output)\n\n    return autoencoder, encoder, decoder","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:37:46.761244Z","iopub.execute_input":"2025-10-28T13:37:46.761500Z","iopub.status.idle":"2025-10-28T13:37:46.770291Z","shell.execute_reply.started":"2025-10-28T13:37:46.761483Z","shell.execute_reply":"2025-10-28T13:37:46.769424Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Training logic","metadata":{}},{"cell_type":"code","source":"# Optimizer\noptimizer=keras.optimizers.Adam(learning_rate=lr)\n\n# Callbacks\nreduce_lr = keras.callbacks.ReduceLROnPlateau(factor = 0.5, patience = patience / 2, verbose=1)\nearly_stop = keras.callbacks.EarlyStopping(patience = patience, verbose = 1, restore_best_weights = True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:37:46.771416Z","iopub.execute_input":"2025-10-28T13:37:46.771632Z","iopub.status.idle":"2025-10-28T13:37:46.787378Z","shell.execute_reply.started":"2025-10-28T13:37:46.771617Z","shell.execute_reply":"2025-10-28T13:37:46.786600Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Compile model","metadata":{}},{"cell_type":"code","source":"autoencoder, encoder, decoder=conv_autoencoder(input_shape=(target_shape[0], target_shape[1], 1), latent_dim=latent_dim)\nautoencoder.compile(optimizer='adam', loss='mse')\nautoencoder.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:37:46.788640Z","iopub.execute_input":"2025-10-28T13:37:46.788931Z","iopub.status.idle":"2025-10-28T13:37:46.871246Z","shell.execute_reply.started":"2025-10-28T13:37:46.788914Z","shell.execute_reply":"2025-10-28T13:37:46.870742Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Data loading","metadata":{}},{"cell_type":"code","source":"def load_images(source_path, target_shape=(128, 256)):\n\n    images=[]\n    for f in os.listdir(source_path):\n        if f.endswith('.png'):\n            image_path=os.path.join(source_path, f)\n            image=Image.open(image_path).convert('L')\n            image=image.resize(target_shape)\n            image_array=np.array(image)/255.0\n            images.append(image_array)\n    return np.array(images)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:37:46.872056Z","iopub.execute_input":"2025-10-28T13:37:46.872307Z","iopub.status.idle":"2025-10-28T13:37:46.876551Z","shell.execute_reply.started":"2025-10-28T13:37:46.872281Z","shell.execute_reply":"2025-10-28T13:37:46.875946Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"images=load_images(save_path, target_shape=target_shape).reshape(-1, target_shape[0], target_shape[1], 1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:37:46.877961Z","iopub.execute_input":"2025-10-28T13:37:46.878235Z","iopub.status.idle":"2025-10-28T13:37:53.686761Z","shell.execute_reply.started":"2025-10-28T13:37:46.878212Z","shell.execute_reply":"2025-10-28T13:37:53.686173Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Training","metadata":{}},{"cell_type":"code","source":"history=autoencoder.fit(\n    images,\n    images,\n    epochs=epochs,\n    batch_size=batch_size,\n    validation_split=0.2,\n    callbacks=[early_stop, reduce_lr],\n    verbose=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:37:53.687366Z","iopub.execute_input":"2025-10-28T13:37:53.687546Z","iopub.status.idle":"2025-10-28T13:44:52.869244Z","shell.execute_reply.started":"2025-10-28T13:37:53.687532Z","shell.execute_reply":"2025-10-28T13:44:52.868588Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plotting","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:44:52.870349Z","iopub.execute_input":"2025-10-28T13:44:52.870589Z","iopub.status.idle":"2025-10-28T13:44:52.874146Z","shell.execute_reply.started":"2025-10-28T13:44:52.870571Z","shell.execute_reply":"2025-10-28T13:44:52.873468Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Feature extraction","metadata":{}},{"cell_type":"code","source":"def feature_extraction(spectrograms, encoder, n_clusters=24):\n    \n    X_features=spectrograms.reshape(-1, target_shape[0], target_shape[1], 1)\n    latent_features=encoder.predict(X_features, verbose=0)\n    print(f\"Shape of latent_features: {latent_features.shape}\")\n    normalized_latent_features=StandardScaler().fit_transform(latent_features)\n    kmeans=KMeans(\n        n_clusters=n_clusters,\n        n_init=10,\n        random_state=random_seed,\n        verbose=1\n    )\n    cluster_labels=kmeans.fit_predict(normalized_latent_features)\n    return cluster_labels, latent_features, kmeans","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:44:52.875121Z","iopub.execute_input":"2025-10-28T13:44:52.875442Z","iopub.status.idle":"2025-10-28T13:44:52.887116Z","shell.execute_reply.started":"2025-10-28T13:44:52.875424Z","shell.execute_reply":"2025-10-28T13:44:52.886427Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"cluster_labels, latent_features, kmeans=feature_extraction(images, encoder, num_clusters)\nprint(f\"Found {len(np.unique(cluster_labels))} clusters.\")\n\ncluster_counts=np.bincount(cluster_labels)\nprint(\"Number of files in each cluster:\")\nfor i, count in enumerate(cluster_counts):\n    print(f\"Cluster {i}:\\t{count}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:44:52.888833Z","iopub.execute_input":"2025-10-28T13:44:52.889035Z","iopub.status.idle":"2025-10-28T13:44:56.047783Z","shell.execute_reply.started":"2025-10-28T13:44:52.889008Z","shell.execute_reply":"2025-10-28T13:44:56.046727Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Visualization","metadata":{}},{"cell_type":"code","source":"def visualization(images, cluster_labels, latent_features):\n\n    # t-SNE visualization\n    tsne=TSNE(n_components=2, perplexity=30, random_state=random_seed, verbose=1)\n    features_tsne=tsne.fit_transform(latent_features)\n    plt.figure(figsize=(15, 5))\n    plt.subplot(1, 1, 1)\n    colors=plt.cm.tab20.colors+plt.cm.tab10.colors[:4]\n    cmap=plt.matplotlib.colors.ListedColormap(colors)\n    scatter=plt.scatter(features_tsne[:, 0], features_tsne[:, 1], c=cluster_labels, cmap=cmap, alpha=0.6)\n    plt.colorbar(scatter, ticks=range(num_clusters))\n    plt.title('t-SNE visualization of the clusters')\n    plt.xlabel('t-SNE 1')\n    plt.ylabel('t-SNE 2')\n    plt.show()\n\n    # Example spectrogram from each cluster\n    unique_clusters=np.unique(cluster_labels)\n    for i, c in enumerate(unique_clusters):\n        plt.subplot(4, 6, i+1)\n        cluster_indices=np.where(cluster_labels==c)[0]\n        if len(cluster_indices)>0:\n            plt.imshow(images[cluster_indices[0]], aspect='auto', origin='lower')\n            plt.title(f'Cluster {c}')\n            plt.axis('off')\n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:44:56.048438Z","iopub.execute_input":"2025-10-28T13:44:56.048624Z","iopub.status.idle":"2025-10-28T13:44:56.058374Z","shell.execute_reply.started":"2025-10-28T13:44:56.048608Z","shell.execute_reply":"2025-10-28T13:44:56.057557Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"visualization(images, cluster_labels, latent_features)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:44:56.059653Z","iopub.execute_input":"2025-10-28T13:44:56.059892Z","iopub.status.idle":"2025-10-28T13:45:01.938959Z","shell.execute_reply.started":"2025-10-28T13:44:56.059876Z","shell.execute_reply":"2025-10-28T13:45:01.938031Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Reconstruction","metadata":{}},{"cell_type":"code","source":"def spec_reconstruct(file_path, autoencoder, target_shape=(128, 256)):\n\n    S_norm=spec_gen(file_path, target_shape)\n    input_image=S_norm.reshape(1, target_shape[0], target_shape[1], 1)\n    reconstructed_image=autoencoder.predict(input_image, verbose=1)[0, :, :, 0]\n\n    plt.figure(figsize=(12, 5))\n    plt.subplot(1, 2, 1)\n    plt.imshow(S_norm, aspect='auto', origin='lower', cmap='viridis')\n    plt.title('Original')\n    plt.subplot(1, 2, 2)\n    plt.imshow(reconstructed_image, aspect='auto', origin='lower', cmap='viridis')\n    plt.title('Reconstructed')\n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:45:01.939760Z","iopub.execute_input":"2025-10-28T13:45:01.940014Z","iopub.status.idle":"2025-10-28T13:45:01.947872Z","shell.execute_reply.started":"2025-10-28T13:45:01.939993Z","shell.execute_reply":"2025-10-28T13:45:01.947084Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"example_file=random_files(train_path)[0]\nspec_reconstruct(example_file, autoencoder, target_shape)\nAudio(example_file, rate=sr)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-28T13:47:01.658167Z","iopub.execute_input":"2025-10-28T13:47:01.658842Z","iopub.status.idle":"2025-10-28T13:47:02.521189Z","shell.execute_reply.started":"2025-10-28T13:47:01.658818Z","shell.execute_reply":"2025-10-28T13:47:02.520162Z"}},"outputs":[],"execution_count":null}]}