{ "cells": [ { "cell_type": "code", "execution_count": 71, "metadata": {}, "outputs": [], "source": [ "import os\n", "import torch\n", "import torch.nn as nn\n", "import torch.optim as optim\n", "from torch.utils.data import Dataset, DataLoader\n", "import torchaudio\n", "import numpy as np\n", "import pandas as pd\n", "import torchaudio.transforms as transforms\n", "from pydub import AudioSegment\n", "import matplotlib.pyplot as plt\n", "import IPython\n", "%matplotlib inline\n", "\n", "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")" ] }, { "cell_type": "code", "execution_count": 72, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 72, "metadata": {}, "output_type": "execute_result" } ], "source": [ "IPython.display.Audio(\"./exp1/processed_audio/audio1/audio1_0.wav\")" ] }, { "cell_type": "code", "execution_count": 73, "metadata": {}, "outputs": [], "source": [ "# df work\n", "\n", "# create df with file paths and time points\n", "\n", "# directories to get audio files from\n", "audio_dirs = [\"./data\"]\n", "\n", "# create list of file paths and time points\n", "file_paths = []\n", "time_points = []\n", "sample_rates = []\n", "\n", "for audio_dir in audio_dirs:\n", " for file in os.listdir(audio_dir):\n", " if file.endswith(\".wav\"):\n", " file_paths.append(f\"{audio_dir}/{file}\")\n", " elif file.endswith(\".m4a\"):\n", " file_paths.append(f\"{audio_dir}/{file}\")\n", "\n", "# time point is length of audio file minus 1\n", "for file_path in file_paths:\n", " audio = AudioSegment.from_file(file_path)\n", " time_points.append(audio.duration_seconds - 1)\n", "\n", "for file_path in file_paths:\n", " audio = AudioSegment.from_file(file_path)\n", " sample_rates.append(audio.frame_rate)\n", "\n", "\n", "\n", "# create df\n", "df = pd.DataFrame({\"file_path\": file_paths, \"time_point\": time_points, \"sample_rate\": sample_rates})\n", "\n", "\n", "# train test split\n", "from sklearn.model_selection import train_test_split\n", "\n", "train_df, test_df = train_test_split(df, test_size=0.1, random_state=42)" ] }, { "cell_type": "code", "execution_count": 74, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
file_pathtime_pointsample_rate
0./data/2024-10-12-17-22-31.m4a6.5528000
1./data/2024-10-12-17-47-04.m4a6.5528000
2./data/processed_audio__audio1_audio1_2.wav5.00048000
3./data/2024-10-12-17-29-33.m4a16.9208000
4./data/processed_audio__audio1_audio1_1.wav7.00048000
............
68./data/2024-10-12-17-46-43.m4a8.3448000
69./data/2024-10-12-17-28-24.m4a8.4728000
70./data/processed_audio__audio3_audio4_8.wav6.00048000
71./data/processed_audio__audio2_audio2_5.wav8.00048000
72./data/processed_audio__audio2_audio2_3.wav8.00048000
\n", "

73 rows × 3 columns

\n", "
" ], "text/plain": [ " file_path time_point sample_rate\n", "0 ./data/2024-10-12-17-22-31.m4a 6.552 8000\n", "1 ./data/2024-10-12-17-47-04.m4a 6.552 8000\n", "2 ./data/processed_audio__audio1_audio1_2.wav 5.000 48000\n", "3 ./data/2024-10-12-17-29-33.m4a 16.920 8000\n", "4 ./data/processed_audio__audio1_audio1_1.wav 7.000 48000\n", ".. ... ... ...\n", "68 ./data/2024-10-12-17-46-43.m4a 8.344 8000\n", "69 ./data/2024-10-12-17-28-24.m4a 8.472 8000\n", "70 ./data/processed_audio__audio3_audio4_8.wav 6.000 48000\n", "71 ./data/processed_audio__audio2_audio2_5.wav 8.000 48000\n", "72 ./data/processed_audio__audio2_audio2_3.wav 8.000 48000\n", "\n", "[73 rows x 3 columns]" ] }, "execution_count": 74, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df" ] }, { "cell_type": "code", "execution_count": 75, "metadata": {}, "outputs": [], "source": [ "class SlidingWindowAudioDataset(Dataset):\n", " def __init__(self, df, sample_rate=16000, window_size=1, hop_length=0.1, n_fft=2048, n_mels=128):\n", " self.df = df\n", " self.sample_rate = sample_rate\n", " self.window_size = window_size\n", " self.hop_length = int(hop_length * sample_rate)\n", " self.n_fft = n_fft\n", " self.n_mels = n_mels\n", " \n", " def __len__(self):\n", " return len(self.df)\n", "\n", " def __getitem__(self, idx):\n", " file_path = self.df.iloc[idx]['file_path']\n", " label_point_seconds = self.df.iloc[idx]['time_point']\n", "\n", " # Load audio file\n", " waveform, sr = torchaudio.load(file_path)\n", " \n", " # Convert to mono if stereo\n", " if waveform.shape[0] > 1:\n", " waveform = waveform.mean(dim=0, keepdim=True)\n", "\n", " if sr != self.sample_rate:\n", " resampler = torchaudio.transforms.Resample(sr, self.sample_rate)\n", " waveform = resampler(waveform)\n", "\n", " \n", " # Calculate mel spectrogram\n", " mel_spectrogram = torchaudio.transforms.MelSpectrogram(\n", " sample_rate=self.sample_rate,\n", " n_fft=self.n_fft,\n", " hop_length=self.hop_length,\n", " n_mels=self.n_mels\n", " )(waveform)\n", " \n", " # Apply sliding window\n", " # Debugging information\n", " # print(f\"File: {file_path}\")\n", " # print(f\"Mel Spectrogram shape: {mel_spectrogram.shape}\")\n", " # print(f\"Sample Rate: {self.sample_rate}\")\n", " # print(f\"Hop Length: {self.hop_length}\")\n", " # print(f\"Window Size: {self.window_size}\")\n", " # print(f\"Waveform shape: {len(waveform[0])}\")\n", " \n", " # Calculate number of windows\n", " total_duration = len(waveform[0]) / self.sample_rate\n", " window_duration = self.window_size\n", " hop_duration = self.hop_length / self.sample_rate\n", " \n", " num_windows = max(0, int((total_duration - window_duration) // hop_duration + 1))\n", " \n", " # print(f\"Total Duration: {total_duration:.2f} seconds\")\n", " # print(f\"Window Duration: {window_duration} seconds\")\n", " # print(f\"Hop Duration: {hop_duration:.2f} seconds\")\n", " # print(f\"Calculated Num Windows: {num_windows}\")\n", " \n", " windows = []\n", " labels = []\n", " for i in range(num_windows):\n", " start_time = i * self.hop_length / self.sample_rate\n", " end_time = start_time + self.window_size\n", " \n", " window = mel_spectrogram[:, :, i:i+int(self.window_size * self.sample_rate / self.hop_length)]\n", " # print(window.shape)\n", "\n", " # label point is 85%\n", " # find middle of window and find the percentage based on the label point\n", " middle = (start_time + end_time) / 2\n", " # full time based on 85% mark\n", " full_time = total_duration / (0.85 * 100) * 100\n", " # label point\n", " label = middle / full_time\n", "\n", " windows.append(window)\n", " labels.append(label)\n", "\n", " # print(f\"Actual Num Windows: {len(windows)}\")\n", " # print(\"---\")\n", "\n", " # print shape\n", " # print(torch.stack(windows).shape)\n", " # print(torch.tensor(labels).shape)\n", " return torch.stack(windows), torch.tensor(labels)\n" ] }, { "cell_type": "code", "execution_count": 76, "metadata": {}, "outputs": [], "source": [ "class AudioRNN(nn.Module):\n", " def __init__(self, input_size, hidden_size, output_size, dropout, num_layers=1):\n", " super(AudioRNN, self).__init__()\n", " self.rnn = nn.RNN(input_size, hidden_size, num_layers, batch_first=True)\n", " self.fc = nn.Linear(hidden_size, output_size)\n", " self.hidden_size = hidden_size\n", " self.hidden_state = None\n", " self.hidden_size = hidden_size\n", " self.num_layers = num_layers\n", " self.dropout = nn.Dropout(dropout)\n", "\n", " def reset_hidden_state(self):\n", " self.hidden_state = torch.zeros(self.num_layers, 1, self.hidden_size).to(device)\n", "\n", " def forward(self, x):\n", " out, _ = self.rnn(x, self.hidden_state)\n", " out = self.dropout(out)\n", " out = self.fc(out[:, -1, :])\n", " return out" ] }, { "cell_type": "code", "execution_count": 77, "metadata": {}, "outputs": [], "source": [ "def train(model, device, loader, test_loader, criterion, optimizer, epochs, patience=3):\n", " best_acc = 0\n", " patience_counter = 0\n", " epoch_losses = []\n", " for epoch in range(epochs):\n", " model.train()\n", " running_loss = 0.0\n", " for batch_idx, (data, target) in enumerate(loader):\n", " for i in range(data.size(1)):\n", " # the label we want is only the index i of the target\n", " target_batch = target[:, i].to(device).float()\n", " # get the data for the index i\n", " data_batch = data[:, i, :, :].to(device)\n", "\n", " model.reset_hidden_state()\n", "\n", " data_batch = data_batch.squeeze(1)\n", " data_batch = data_batch.permute(0, 2, 1)\n", "\n", " optimizer.zero_grad()\n", " output = model(data_batch)\n", " output = output.squeeze(1)\n", " \n", " loss = criterion(output, target_batch)\n", " loss.backward()\n", " optimizer.step()\n", " running_loss += loss.item()\n", " \n", " epoch_losses.append(running_loss/(batch_idx+1))\n", " print(f'Epoch {epoch+1}/{epochs}, Loss: {running_loss/(batch_idx+1)}')\n", " # Plotting the training loss\n", " IPython.display.clear_output(wait=True)\n", " plt.plot(epoch_losses, marker='o', linestyle='-', color='b')\n", " plt.xlabel('Steps')\n", " plt.ylabel('Loss')\n", " plt.grid(True)\n", " plt.show()\n", "\n", " print(f\"Epoch: {epoch}\")\n", " print(\"test loss\")\n", " test_acc = test(model, device, test_loader)\n", "\n", " if test_acc > best_acc:\n", " best_acc = test_acc\n", " patience_counter = 0\n", " else:\n", " patience_counter += 1\n", "\n", " if patience_counter > patience:\n", " print(\"Early Stopping\")\n", " break\n", "\n", " print(\"train loss\")\n", " train_acc = test(model, device, loader)\n", "\n", "\n", "def test(model, device, loader):\n", " total_loss = 0\n", " total_correct = 0\n", " total_no = 0\n", " leeway = 0.1\n", " crit = torch.nn.MSELoss()\n", " model.eval()\n", " with torch.no_grad():\n", " for data, target in loader:\n", " for i in range(data.size(1)):\n", " data_batch = data[:, i, :, :].to(device)\n", " target_batch = target[:, i].to(device)\n", "\n", " # reset hidden state\n", " model.reset_hidden_state()\n", "\n", " # drop x y dimension\n", " data_batch = data_batch.squeeze(1)\n", " # wrong dimension order swap y z\n", " data_batch = data_batch.permute(0, 2, 1)\n", " # feed to model\n", " output = model(data_batch)\n", " output = output.squeeze(1)\n", "\n", " if abs(output.item() - target_batch.item()) < leeway:\n", " total_correct += 1\n", " total_no += 1\n", "\n", " # get accuracy with the output and target float\n", "\n", " loss = crit(output, target_batch)\n", " total_loss += loss.item()\n", " \n", " avg_loss = total_loss / total_no\n", " print(f'Validation Loss: {avg_loss}')\n", " print(f'Accuracy: {total_correct / total_no}')\n", " return total_correct / total_no" ] }, { "cell_type": "code", "execution_count": 78, "metadata": {}, "outputs": [], "source": [ "train_dataset = SlidingWindowAudioDataset(train_df, \n", " sample_rate=16000)\n", "\n", "test_dataset = SlidingWindowAudioDataset(test_df,\n", " sample_rate=16000) \n", "\n", "batch_size = 1\n", "train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)\n", "test_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=False)\n", "\n", "# Model configuration\n", "input_dim = 128 # Number of mel frequency bins\n", "hidden_dim = 128\n", "output_dim = 1\n", "num_layers = 1\n", "dropout = 0.3\n", "learning_rate = 0.00003\n", "\n", "model = AudioRNN(input_dim, hidden_dim, output_dim, dropout, num_layers).to(device)\n", "\n", "# self, input_size, hidden_size, output_size, num_layers=1\n", "\n", "criterion = nn.MSELoss()\n", "\n", "\n", "optimizer = optim.Adam(model.parameters(), lr=learning_rate, weight_decay=0.0003)" ] }, { "cell_type": "code", "execution_count": 79, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Validation Loss: 0.629760848049892\n", "Accuracy: 0.07122145704965567\n" ] }, { "data": { "text/plain": [ "0.07122145704965567" ] }, "execution_count": 79, "metadata": {}, "output_type": "execute_result" } ], "source": [ "test(model, device, test_loader)" ] }, { "cell_type": "code", "execution_count": 80, "metadata": {}, "outputs": [ { "data": { "image/png": "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DOTkqF0KIiIi7fMrkGnRogVWrlyJ48ePY82aNejevTsA4Pfff0dcXFxQC1hV/P678rSlpUD//gxmiIiIvPErkHnppZcwfvx4pKeno3379sjIyAAgW2fatGkT1AJWFf6s1jt6NDeQJCIi8sSvQGbAgAE4duwYfvjhB6xZs8Z+vEuXLpg1a1bQCleVZGYCiYm+nXPiBNeUISIi8sSvdWQAICkpCUlJSfZdsFNSUrgYngdGIzB3LjBwoG/ncbYTERGRe361yFitVkydOhXx8fFIS0tDWloa6tSpg7///e+wWq3BLmOVcf/9wL33+nYO15QhIiJyz68Wmeeffx4LFizA9OnTcccddwAAtm7dismTJ+Pq1av4xz/+EdRCViUTJgCrVytPzzEyRERE7vkVyHzwwQf497//bd/1GgBat26Nxo0b48knn2Qg40HHjr6l37wZ+N+kMCIiIqrEr66lCxcuoFWrVk7HW7VqhQtK1+GvprZt8y39t9+GphxERERVgV+BzM0334y3337b6fjbb7+N1q1bB1yoqszXwbs//cTuJSIiInf86lqaMWMG7rvvPqxfv96+hsz27dtx/PhxrPZlAEg15Ot6MkVFcgp2584hKQ4REZGu+dUic9ddd+H//b//h379+qGgoAAFBQXIzs7GL7/8go8++ijYZaxSOnYEDAbfzuEUbCIiItf8XkcmOTnZaVDvnj17sGDBAsyfPz/gglVV27YBQvh2DqdgExERueZXiwz5j60rREREwcNAJsz82XPJlw0niYiIqhMGMmGWmQmkpPg2ToZdS0RERK75NEYmOzvb4+sFBQWBlKVaMBqBN98EBgxQuyRERET651MgEx8f7/X1Bx98MKACVQfZ2cDnnwMjRgBK1g9k1xIREZFrPgUyCxcuDFU5qp3sbKBWLaBHD+9p2bVERETkGsfI6MDmzWqXgIiISJsYyKgoL09Zujff5DYFRERErjCQ0QHbNgVERETkiIGMinzZP+nkyZAVg4iISLcYyKioc2cgNlZZ2rNnQ1oUIiIiXWIgoyKjEXj0UWVp69cPbVmIiIj0iIGMyvr0UZYuKSm05SAiItIjBjIqUzobibOWiIiInDGQUZnS2UjvvRfachAREekRAxmd+L//Y6sMERFRZaoGMlu2bEHv3r2RnJwMg8GAlStXOrwuhMBLL72ERo0aISYmBl27dsWB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", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Epoch: 30\n", "test loss\n", "Validation Loss: 0.011056410072496364\n", "Accuracy: 0.7776368249365713\n", "Early Stopping\n" ] } ], "source": [ "train(model, device, train_loader, test_loader, criterion, optimizer, epochs=100)" ] }, { "cell_type": "code", "execution_count": 81, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Validation Loss: 0.011056410072496364\n", "Accuracy: 0.7776368249365713\n" ] }, { "data": { "text/plain": [ "0.7776368249365713" ] }, "execution_count": 81, "metadata": {}, "output_type": "execute_result" } ], "source": [ "test(model, device, test_loader)" ] }, { "cell_type": "code", "execution_count": 82, "metadata": {}, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "model\n", "\n", "\n", "\n", "0\n", "\n", "\n", "input-tensor\n", "depth:0\n", "\n", "(1, 10, 128)\n", "\n", "\n", "\n", "1\n", "\n", "\n", "RNN\n", "depth:1\n", "\n", "input:\n", "\n", "(1, 10, 128) \n", "\n", "output: \n", "\n", "(1, 10, 128), (1, 1, 128) \n", "\n", "\n", "\n", "0->1\n", "\n", "\n", "\n", "\n", "\n", "2\n", "\n", "\n", "Dropout\n", "depth:1\n", "\n", "input:\n", "\n", "(1, 10, 128) \n", "\n", "output: \n", "\n", "(1, 10, 128) \n", "\n", "\n", "\n", "1->2\n", "\n", "\n", "\n", "\n", "\n", "3\n", "\n", "\n", "__getitem__\n", "depth:1\n", "\n", "input:\n", "\n", "(1, 10, 128) \n", "\n", "output: \n", "\n", "(1, 128) \n", "\n", "\n", "\n", "2->3\n", "\n", "\n", "\n", "\n", "\n", "4\n", "\n", "\n", "Linear\n", "depth:1\n", "\n", "input:\n", "\n", "(1, 128) \n", "\n", "output: \n", "\n", "(1, 1) \n", "\n", "\n", "\n", "3->4\n", "\n", "\n", "\n", "\n", "\n", "5\n", "\n", "\n", "output-tensor\n", "depth:0\n", "\n", "(1, 1)\n", "\n", "\n", "\n", "4->5\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "execution_count": 82, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# from torchviz import make_dot\n", "from torchview import draw_graph\n", "import matplotlib.pyplot as plt\n", "# Visualize the model\n", "# x = torch.randn(1, 10, input_size)\n", "# y = model(x)\n", "# make_dot(y, params=dict(list(model.named_parameters()) + [('x', x)])).render(\"rnn_torchviz\", format=\"png\")\n", "\n", "# Display the generated graph\n", "# img = plt.imread(\"rnn_torchviz.png\")\n", "# plt.imshow(img)\n", "# plt.axis('off')\n", "\n", "# Visualize the model\n", "x = torch.randn(1, 10, input_dim).to(device)\n", "y = model(x)\n", "graph = draw_graph(model, input_size=(1, 10, input_dim), device=device)\n", "graph.visual_graph.render(\"rnn_torchview\", format=\"png\")\n", "graph.visual_graph\n", "\n", "# Display the generated graph\n", "# img = plt.imread(\"rnn_torchview.png\")\n", "# plt.imshow(img)\n", "# plt.axis('off')\n", "# plt.show()" ] }, { "cell_type": "code", "execution_count": 83, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "==========================================================================================\n", "Layer (type:depth-idx) Output Shape Param #\n", "==========================================================================================\n", "AudioRNN [1, 1] --\n", "├─RNN: 1-1 [1, 10, 128] 33,024\n", "├─Dropout: 1-2 [1, 10, 128] --\n", "├─Linear: 1-3 [1, 1] 129\n", "==========================================================================================\n", "Total params: 33,153\n", "Trainable params: 33,153\n", "Non-trainable params: 0\n", "Total mult-adds (Units.MEGABYTES): 0.33\n", "==========================================================================================\n", "Input size (MB): 0.01\n", "Forward/backward pass size (MB): 0.01\n", "Params size (MB): 0.13\n", "Estimated Total Size (MB): 0.15\n", "==========================================================================================" ] }, "execution_count": 83, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from torchinfo import summary\n", "\n", "summary(model, input_size=(1, 10, input_dim), device=device)" ] }, { "cell_type": "code", "execution_count": 84, "metadata": {}, "outputs": [], "source": [ "# save model\n", "# name is time\n", "import time\n", "torch.save(model.state_dict(), f\"./model_{time.time()}.pth\")" ] } ], "metadata": { "kernelspec": { "display_name": "venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.6" } }, "nbformat": 4, "nbformat_minor": 2 }