{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":8900,"databundleVersionId":862232,"sourceType":"competition"}],"dockerImageVersionId":31234,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## Подключение библиотек","metadata":{}},{"cell_type":"code","source":"import os\n\nimport numpy as np\nimport pandas as pd\nimport librosa\nimport librosa.display\n\nimport matplotlib.pyplot as plt\n\nfrom tqdm import tqdm\nimport cv2\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import LabelBinarizer\nfrom sklearn.metrics import classification_report, accuracy_score\n\nimport tensorflow as tf\nfrom tensorflow.keras.applications import ResNet50\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Dense, GlobalAveragePooling2D, Dropout\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.callbacks import ModelCheckpoint, ReduceLROnPlateau\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-28T20:44:00.322186Z","iopub.execute_input":"2026-01-28T20:44:00.322717Z","iopub.status.idle":"2026-01-28T20:44:00.327535Z","shell.execute_reply.started":"2026-01-28T20:44:00.322683Z","shell.execute_reply":"2026-01-28T20:44:00.326817Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Задание констант","metadata":{}},{"cell_type":"code","source":"# Пути к данным\nTRAIN_CSV = '/kaggle/input/freesound-audio-tagging/train.csv'\nTRAIN_DIR = '/kaggle/input/freesound-audio-tagging/audio_train/'\n\nSAMPLE_SUBMISSION_CSV = '/kaggle/input/freesound-audio-tagging/sample_submission.csv'\nTEST_DIR = '/kaggle/input/freesound-audio-tagging/audio_test/'\n\n# Параметры аудио\n# Частота дискретизации\nSAMPLE_RATE = 44100\n # Длительность в секундах\nDURATION = 3\n# Общее количество точек\nSAMPLES = SAMPLE_RATE * DURATION \n\n# Параметры изображений\n# Размер изображения\nIMG_SIZE = 128\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-28T20:44:00.329210Z","iopub.execute_input":"2026-01-28T20:44:00.329540Z","iopub.status.idle":"2026-01-28T20:44:00.363249Z","shell.execute_reply.started":"2026-01-28T20:44:00.329487Z","shell.execute_reply":"2026-01-28T20:44:00.362535Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Функции для предобработки данных","metadata":{}},{"cell_type":"code","source":"# Функция для приведения аудиосигнала к фиксированной длине\ndef ensure_sample_length(y, target_length):\n    if len(y) > target_length:\n        return y[:target_length]\n    else:\n        padding = target_length - len(y)\n        return np.pad(y, (0, padding), 'constant')\n\n# Функция для извлечения признаков из аудиофайла\ndef get_features(file_path):\n    try:\n        # Загружаем аудио\n        y, sr = librosa.load(file_path, sr=SAMPLE_RATE)\n\n        # Приводим аудио к нужной длине\n        y = ensure_sample_length(y, SAMPLES)\n\n        # Извлекаем мел-спектрограмму\n        melspec = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=128)\n        melspec_db = librosa.power_to_db(melspec, ref=np.max)\n\n        # Извлекаем скорость измениний мэл-спектрограммы (первая производная)\n        delta = librosa.feature.delta(melspec_db)\n\n        # Извлекаем ускорение изменений мэл-спектрограммы (вторая производная)\n        delta2 = librosa.feature.delta(melspec_db, order=2)\n\n        # Собираем 3 признака в одно изображение\n        img = np.stack([melspec_db, delta, delta2], axis=-1)\n        \n        # Min-Max нормализуем значения от 0 до 1\n        min_val = img.min()\n        max_val = img.max()\n        if (max_val - min_val) > 0:\n            img = (img - min_val) / (max_val - min_val)\n        \n        # Изменяем размер до квадратного\n        img_resized = cv2.resize(img, (IMG_SIZE, IMG_SIZE))\n        \n        return img_resized\n\n    except Exception as e:\n        print(f\"Error: {e}\")\n        return np.zeros((IMG_SIZE, IMG_SIZE, 3))\n\n# Демонстрация одного примера\nprint(\"Демонстрация одного примера:\")\ndf = pd.read_csv(TRAIN_CSV)\nsample_file = os.path.join(TRAIN_DIR, df.iloc[0]['fname'])\nsample_img = get_features(sample_file)\n\nplt.figure(figsize=(10, 3))\nplt.subplot(1, 3, 1); plt.title(\"Mel-Spec\"); plt.imshow(sample_img[:,:,0])\nplt.subplot(1, 3, 2); plt.title(\"Delta\"); plt.imshow(sample_img[:,:,1])\nplt.subplot(1, 3, 3); plt.title(\"Delta-Delta\"); plt.imshow(sample_img[:,:,2])\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-28T20:44:00.365060Z","iopub.execute_input":"2026-01-28T20:44:00.365302Z","iopub.status.idle":"2026-01-28T20:44:00.776249Z","shell.execute_reply.started":"2026-01-28T20:44:00.365281Z","shell.execute_reply":"2026-01-28T20:44:00.775566Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Загрузка и подготовка данных","metadata":{}},{"cell_type":"code","source":"# Считываем тренировочный датасет\ndf = pd.read_csv(TRAIN_CSV)\n\n# Отделяем только верифицированные данные\ndf_clean = df[df['manually_verified'] == 1].reset_index(drop=True)\nprint(f\"Используем: {len(df_clean)} верифицированных из {len(df)}\")\n\n# Создаем пустые списки для картинок и меток классов\nX = []\ny = []\n\n# Запускаем цикл обработки файлов с прогресс-баром\nprint(\"Предобработка данных:\")\nfor i in tqdm(range(len(df_clean))):\n    # Получаем путь к файлу\n    row = df_clean.iloc[i]\n    file_path = os.path.join(TRAIN_DIR, row['fname'])\n    \n    # Извлекаем признаки\n    img = get_features(file_path)\n    \n    # Добавляем в массив данных, если картинка не пустая\n    if np.sum(img) != 0:\n        X.append(img)\n        y.append(row['label'])\n\n# Превращаем списки в NumPy массивы\nX = np.array(X)\n\n# Кодируем метки в формате One-Hot Encoding\nlb = LabelBinarizer()\ny_onehot = lb.fit_transform(y)\n\n# Разделение на Train и Validation\nX_train, X_val, y_train, y_val = train_test_split(X, y_onehot, test_size=0.2, random_state=42, stratify=y_onehot)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-28T20:44:23.679848Z","iopub.execute_input":"2026-01-28T20:44:23.680375Z","iopub.status.idle":"2026-01-28T20:46:27.773159Z","shell.execute_reply.started":"2026-01-28T20:44:23.680346Z","shell.execute_reply":"2026-01-28T20:46:27.772574Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Создание модели","metadata":{}},{"cell_type":"code","source":"def build_model(num_classes):\n    # Загружаем ResNet50 без верхнего слоя, предобученный на ImageNet\n    base_model = ResNet50(weights='imagenet', include_top=False, input_shape=(IMG_SIZE, IMG_SIZE, 3))\n    \n    # Разрешаем обучение весов базовой модели\n    base_model.trainable = True\n\n    # Собираем итоговую архитектуру\n    model = Sequential([\n        base_model,\n        GlobalAveragePooling2D(),                # Сворачиваем карты признаков\n        Dropout(0.5),                            # Выключаем половину нейронов для защиты от переобучения\n        Dense(1024, activation='relu'),\n        Dropout(0.3),\n        Dense(num_classes, activation='softmax')\n    ])\n    \n    return model\n\nmodel = build_model(y_onehot.shape[1])\n\n# Компилируем модель с метриками и функцией потерь\n# Используем низкий learning rate, так как модель уже предобучена\nmodel.compile(optimizer=Adam(learning_rate=1e-4),\n              loss='categorical_crossentropy',\n              metrics=['accuracy'])\n\n# Выводим структуру модели в консоль\nmodel.summary()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-28T20:46:38.721815Z","iopub.execute_input":"2026-01-28T20:46:38.722552Z","iopub.status.idle":"2026-01-28T20:46:44.600423Z","shell.execute_reply.started":"2026-01-28T20:46:38.722518Z","shell.execute_reply":"2026-01-28T20:46:44.599895Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Обучение","metadata":{}},{"cell_type":"code","source":"# Добавляем чекпоинты для выбора лучшей модели и уменьшаем шаг обучения, если вышли на плато\ncheckpoint = ModelCheckpoint(\"best_audio_model.keras\", monitor='val_accuracy', save_best_only=True, verbose=1)\nreduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, verbose=1)\n\n# Параметры запуска обучения\nBATCH_SIZE = 32\nEPOCHS = 20\n\n# Тренируем модель\nhistory = model.fit(\n    X_train, y_train,\n    validation_data=(X_val, y_val),\n    epochs=EPOCHS,\n    batch_size=BATCH_SIZE,\n    callbacks=[checkpoint, reduce_lr],\n    verbose=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-28T20:46:50.898300Z","iopub.execute_input":"2026-01-28T20:46:50.898715Z","iopub.status.idle":"2026-01-28T20:52:32.715574Z","shell.execute_reply.started":"2026-01-28T20:46:50.898686Z","shell.execute_reply":"2026-01-28T20:52:32.714894Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Оценка результатов обучения","metadata":{}},{"cell_type":"code","source":"# Загружаем лучшие веса\nmodel.load_weights(\"best_audio_model.keras\")\n\n# Делаем тестовый прогон на валидации\ny_pred_prob = model.predict(X_val)\ny_pred_indices = np.argmax(y_pred_prob, axis=1)\ny_val_indices = np.argmax(y_val, axis=1)\n\nacc = accuracy_score(y_val_indices, y_pred_indices)\nprint(f\"Итоговая точность: {acc:.2f}\")\n\n# Выводим графики обучения\nplt.figure(figsize=(12, 4))\nplt.subplot(1, 2, 1)\nplt.plot(history.history['accuracy'], label='Train')\nplt.plot(history.history['val_accuracy'], label='Val')\nplt.title('Accuracy')\nplt.legend()\n\nplt.subplot(1, 2, 2)\nplt.plot(history.history['loss'], label='Train')\nplt.plot(history.history['val_loss'], label='Val')\nplt.title('Loss')\nplt.legend()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-28T20:52:59.619955Z","iopub.execute_input":"2026-01-28T20:52:59.620582Z","iopub.status.idle":"2026-01-28T20:53:09.544822Z","shell.execute_reply.started":"2026-01-28T20:52:59.620551Z","shell.execute_reply":"2026-01-28T20:53:09.544073Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Классификация","metadata":{}},{"cell_type":"code","source":"# Загружаем шаблон результирующего файла\ntest_df = pd.read_csv(SAMPLE_SUBMISSION_CSV)\n\nX_test = []\nvalid_indices = []\n\nprint(\"Предобработка тестовых данных:\")\nfor i in tqdm(range(len(test_df))):\n    fname = test_df.iloc[i]['fname']\n    file_path = os.path.join(TEST_DIR, fname)\n    \n    img = get_features(file_path)\n    \n    # Собираем батч\n    X_test.append(img)\n    valid_indices.append(i)\n\nX_test = np.array(X_test)\n\nprint(\"Предсказание...\")\n# Получаем вероятности для каждого класса\npreds = model.predict(X_test, batch_size=32, verbose=1)\n\ntop3_indices = np.argsort(preds, axis=1)[:, :-4:-1]\n\n# Собираем результаты в строку через пробел\npredicted_labels = []\nfor i in range(len(top3_indices)):\n    indices = top3_indices[i]\n    # Преобразуем индексы обратно в названия классов\n    labels = [lb.classes_[idx] for idx in indices]\n    predicted_labels.append(\" \".join(labels))\n\n# Записываем в DataFrame\ntest_df.loc[valid_indices, 'label'] = predicted_labels\n\n# Сохраняем результат\nsubmission_file = 'submission.csv'\ntest_df.to_csv(submission_file, index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-28T20:54:02.336805Z","iopub.execute_input":"2026-01-28T20:54:02.337338Z","iopub.status.idle":"2026-01-28T20:59:16.384073Z","shell.execute_reply.started":"2026-01-28T20:54:02.337308Z","shell.execute_reply":"2026-01-28T20:59:16.383244Z"}},"outputs":[],"execution_count":null}]}