{"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":"gpu","dataSources":[{"sourceId":8900,"databundleVersionId":862232,"sourceType":"competition"}],"dockerImageVersionId":31153,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## Lab 1: Freesound General-Purpose Audio Tagging Challenge","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport librosa\nimport librosa.display\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom sklearn.preprocessing import LabelEncoder\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import classification_report, confusion_matrix\nimport tensorflow as tf\nfrom tensorflow.keras import layers, models, regularizers\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\nimport os\nimport warnings\nwarnings.filterwarnings('ignore')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:04:30.886083Z","iopub.execute_input":"2025-10-22T11:04:30.886720Z","iopub.status.idle":"2025-10-22T11:04:30.891059Z","shell.execute_reply.started":"2025-10-22T11:04:30.886698Z","shell.execute_reply":"2025-10-22T11:04:30.890404Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Правильные пути к данным\ntrain_csv_path = \"../input/freesound-audio-tagging/train.csv\"\ntrain_audio_path = \"../input/freesound-audio-tagging/audio_train/\"\ntest_audio_path = \"../input/freesound-audio-tagging/audio_test/\"\nsample_submission_path = \"../input/freesound-audio-tagging/sample_submission.csv\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:04:30.892796Z","iopub.execute_input":"2025-10-22T11:04:30.893087Z","iopub.status.idle":"2025-10-22T11:04:30.909883Z","shell.execute_reply.started":"2025-10-22T11:04:30.893071Z","shell.execute_reply":"2025-10-22T11:04:30.909270Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Загрузка данных\ntrain_df = pd.read_csv(train_csv_path)\nsample_submission = pd.read_csv(sample_submission_path)\n\nprint(\"Размер тренировочных данных:\", train_df.shape)\nprint(\"Уникальные классы:\", train_df['label'].nunique())\nprint(\"\\nПервые 5 записей:\")\nprint(train_df.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:04:30.911060Z","iopub.execute_input":"2025-10-22T11:04:30.911253Z","iopub.status.idle":"2025-10-22T11:04:30.944359Z","shell.execute_reply.started":"2025-10-22T11:04:30.911239Z","shell.execute_reply":"2025-10-22T11:04:30.943624Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Анализ данных\nprint(\"\\nРаспределение классов:\")\nclass_distribution = train_df['label'].value_counts()\nprint(class_distribution)\n\nprint(f\"\\nПроверка ручной верификации:\")\nprint(train_df['manually_verified'].value_counts())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:04:30.944994Z","iopub.execute_input":"2025-10-22T11:04:30.945166Z","iopub.status.idle":"2025-10-22T11:04:30.951663Z","shell.execute_reply.started":"2025-10-22T11:04:30.945152Z","shell.execute_reply":"2025-10-22T11:04:30.950829Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Визуализация распределения классов\nplt.figure(figsize=(15, 8))\nclass_distribution.plot(kind='bar')\nplt.title('Распределение аудио классов')\nplt.xlabel('Классы')\nplt.ylabel('Количество')\nplt.xticks(rotation=90)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:04:30.952427Z","iopub.execute_input":"2025-10-22T11:04:30.952694Z","iopub.status.idle":"2025-10-22T11:04:31.345230Z","shell.execute_reply.started":"2025-10-22T11:04:30.952674Z","shell.execute_reply":"2025-10-22T11:04:31.344425Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Функция для извлечения Mel-спектрограмм\ndef extract_mel_spectrogram(file_path, duration=3, sr=22050, n_mels=128):\n    try:\n        # Загружаем аудиофайл\n        audio, sample_rate = librosa.load(file_path, duration=duration, sr=sr)\n        \n        # Если аудио короче duration, дополняем нулями\n        if len(audio) < sr * duration:\n            audio = np.pad(audio, (0, max(0, sr * duration - len(audio))), mode='constant')\n        \n        # Создаем Mel-спектрограмму\n        mel_spectrogram = librosa.feature.melspectrogram(\n            y=audio, sr=sr, n_mels=n_mels, fmax=8000\n        )\n        mel_spectrogram_db = librosa.power_to_db(mel_spectrogram, ref=np.max)\n        \n        # Нормализуем спектрограмму\n        mel_spectrogram_db = (mel_spectrogram_db - mel_spectrogram_db.mean()) / (mel_spectrogram_db.std() + 1e-8)\n        \n        return mel_spectrogram_db\n    except Exception as e:\n        print(f\"Ошибка при обработке файла {file_path}: {str(e)}\")\n        return None","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:04:31.346848Z","iopub.execute_input":"2025-10-22T11:04:31.347051Z","iopub.status.idle":"2025-10-22T11:04:31.352686Z","shell.execute_reply.started":"2025-10-22T11:04:31.347036Z","shell.execute_reply":"2025-10-22T11:04:31.351945Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Функция для извлечения расширенного набора признаков\ndef extract_comprehensive_features(file_path, duration=3, sr=22050):\n    try:\n        audio, sample_rate = librosa.load(file_path, duration=duration, sr=sr)\n        \n        # Дополняем нулями если нужно\n        if len(audio) < sr * duration:\n            audio = np.pad(audio, (0, max(0, sr * duration - len(audio))), mode='constant')\n        \n        # Mel-спектрограмма\n        mel_spec = librosa.feature.melspectrogram(y=audio, sr=sr, n_mels=128)\n        mel_spec_db = librosa.power_to_db(mel_spec, ref=np.max)\n        \n        # MFCC\n        mfccs = librosa.feature.mfcc(y=audio, sr=sr, n_mfcc=40)\n        mfccs_mean = np.mean(mfccs, axis=1)\n        \n        # Chroma features\n        chroma = librosa.feature.chroma_stft(y=audio, sr=sr)\n        chroma_mean = np.mean(chroma, axis=1)\n        \n        # Spectral contrast\n        spectral_contrast = librosa.feature.spectral_contrast(y=audio, sr=sr)\n        spectral_contrast_mean = np.mean(spectral_contrast, axis=1)\n        \n        # Tonnetz features\n        tonnetz = librosa.feature.tonnetz(y=audio, sr=sr)\n        tonnetz_mean = np.mean(tonnetz, axis=1)\n        \n        # Статистические признаки\n        spectral_centroid = librosa.feature.spectral_centroid(y=audio, sr=sr)\n        spectral_rolloff = librosa.feature.spectral_rolloff(y=audio, sr=sr)\n        zero_crossing_rate = librosa.feature.zero_crossing_rate(y=audio)\n        \n        statistical_features = np.array([\n            spectral_centroid.mean(), spectral_centroid.std(),\n            spectral_rolloff.mean(), spectral_rolloff.std(),\n            zero_crossing_rate.mean(), zero_crossing_rate.std(),\n            np.mean(audio), np.std(audio), np.max(audio), np.min(audio)\n        ])\n        \n        # Объединяем все признаки\n        all_features = np.concatenate([\n            mfccs_mean, chroma_mean, spectral_contrast_mean, \n            tonnetz_mean, statistical_features\n        ])\n        \n        return {\n            'mel_spectrogram': mel_spec_db,\n            'features': all_features\n        }\n    except Exception as e:\n        print(f\"Ошибка при обработке файла {file_path}: {str(e)}\")\n        return None","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:04:31.353476Z","iopub.execute_input":"2025-10-22T11:04:31.354055Z","iopub.status.idle":"2025-10-22T11:04:31.371122Z","shell.execute_reply.started":"2025-10-22T11:04:31.354031Z","shell.execute_reply":"2025-10-22T11:04:31.370506Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"physical_devices = tf.config.list_physical_devices('GPU')\nif len(physical_devices) > 0:\n    tf.config.experimental.set_memory_growth(physical_devices[0], True)\n    print(\"GPU is available and configured\")\nelse:\n    print(\"GPU not available\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:04:31.371907Z","iopub.execute_input":"2025-10-22T11:04:31.372066Z","iopub.status.idle":"2025-10-22T11:04:31.390018Z","shell.execute_reply.started":"2025-10-22T11:04:31.372051Z","shell.execute_reply":"2025-10-22T11:04:31.389478Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Извлекаем Mel-спектрограммы для тренировочных данных\nprint(\"Извлечение Mel-спектрограмм из тренировочных данных...\")\nspectrograms = []\nfeatures_list = []\nlabels = []\n\nfor index, row in train_df.iterrows():\n    if index % 100 == 0:\n        print(f\"Обработано {index}/{len(train_df)} файлов\")\n    \n    file_path = os.path.join(train_audio_path, row['fname'])\n    result = extract_comprehensive_features(file_path)\n    \n    if result is not None:\n        # Добавляем размерность канала для CNN\n        spectrogram = np.expand_dims(result['mel_spectrogram'], axis=-1)\n        spectrograms.append(spectrogram)\n        features_list.append(result['features'])\n        labels.append(row['label'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:04:31.390706Z","iopub.execute_input":"2025-10-22T11:04:31.390927Z","iopub.status.idle":"2025-10-22T11:25:17.876970Z","shell.execute_reply.started":"2025-10-22T11:04:31.390906Z","shell.execute_reply":"2025-10-22T11:25:17.876315Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Преобразуем в numpy массивы\nX_spectrograms = np.array(spectrograms)\nX_features = np.array(features_list)\ny = np.array(labels)\n\nprint(f\"Форма спектрограмм: {X_spectrograms.shape}\")\nprint(f\"Форма дополнительных признаков: {X_features.shape}\")\nprint(f\"Форма меток: {y.shape}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:25:17.877717Z","iopub.execute_input":"2025-10-22T11:25:17.877950Z","iopub.status.idle":"2025-10-22T11:25:18.083511Z","shell.execute_reply.started":"2025-10-22T11:25:17.877931Z","shell.execute_reply":"2025-10-22T11:25:18.082726Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Кодируем метки\nlabel_encoder = LabelEncoder()\ny_encoded = label_encoder.fit_transform(y)\n\nprint(f\"Закодированные классы: {label_encoder.classes_}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:25:18.084439Z","iopub.execute_input":"2025-10-22T11:25:18.084802Z","iopub.status.idle":"2025-10-22T11:25:18.091167Z","shell.execute_reply.started":"2025-10-22T11:25:18.084777Z","shell.execute_reply":"2025-10-22T11:25:18.090601Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Разделяем данные на тренировочную и валидационную выборки\nX_train_spec, X_val_spec, X_train_feat, X_val_feat, y_train, y_val = train_test_split(\n    X_spectrograms, X_features, y_encoded, test_size=0.2, random_state=42, stratify=y_encoded\n)\n\nprint(f\"Тренировочные спектрограммы: {X_train_spec.shape}\")\nprint(f\"Валидационные спектрограммы: {X_val_spec.shape}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:25:18.091943Z","iopub.execute_input":"2025-10-22T11:25:18.092148Z","iopub.status.idle":"2025-10-22T11:25:18.289734Z","shell.execute_reply.started":"2025-10-22T11:25:18.092134Z","shell.execute_reply":"2025-10-22T11:25:18.288899Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Улучшенная CNN архитектура с двумя входами\ndef create_advanced_cnn_model(spectrogram_shape, feature_shape, num_classes):\n    # Вход для спектрограмм\n    spec_input = layers.Input(shape=spectrogram_shape, name='spectrogram_input')\n    \n    # CNN ветка для спектрограмм\n    x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(spec_input)\n    x = layers.BatchNormalization()(x)\n    x = layers.MaxPooling2D((2, 2))(x)\n    x = layers.Dropout(0.25)(x)\n    \n    x = layers.Conv2D(64, (3, 3), activation='relu', padding='same')(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.MaxPooling2D((2, 2))(x)\n    x = layers.Dropout(0.25)(x)\n    \n    x = layers.Conv2D(128, (3, 3), activation='relu', padding='same')(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.MaxPooling2D((2, 2))(x)\n    x = layers.Dropout(0.25)(x)\n    \n    x = layers.Conv2D(256, (3, 3), activation='relu', padding='same')(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.GlobalAveragePooling2D()(x)\n    x = layers.Dropout(0.5)(x)\n    \n    # Вход для дополнительных признаков\n    feat_input = layers.Input(shape=(feature_shape,), name='feature_input')\n    y = layers.Dense(128, activation='relu')(feat_input)\n    y = layers.BatchNormalization()(y)\n    y = layers.Dropout(0.3)(y)\n    y = layers.Dense(64, activation='relu')(y)\n    y = layers.BatchNormalization()(y)\n    y = layers.Dropout(0.3)(y)\n    \n    # Объединяем обе ветки\n    combined = layers.concatenate([x, y])\n    \n    # Полносвязные слои\n    z = layers.Dense(512, activation='relu', \n                    kernel_regularizer=regularizers.l2(0.001))(combined)\n    z = layers.BatchNormalization()(z)\n    z = layers.Dropout(0.5)(z)\n    \n    z = layers.Dense(256, activation='relu', \n                    kernel_regularizer=regularizers.l2(0.001))(z)\n    z = layers.BatchNormalization()(z)\n    z = layers.Dropout(0.5)(z)\n    \n    outputs = layers.Dense(num_classes, activation='softmax')(z)\n    \n    model = models.Model(inputs=[spec_input, feat_input], outputs=outputs)\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:25:18.290449Z","iopub.execute_input":"2025-10-22T11:25:18.290669Z","iopub.status.idle":"2025-10-22T11:25:18.299607Z","shell.execute_reply.started":"2025-10-22T11:25:18.290651Z","shell.execute_reply":"2025-10-22T11:25:18.299002Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Создаем модель\nspectrogram_shape = X_train_spec.shape[1:]\nfeature_shape = X_train_feat.shape[1]\nnum_classes = len(label_encoder.classes_)\n\nmodel = create_advanced_cnn_model(spectrogram_shape, feature_shape, num_classes)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:25:18.300323Z","iopub.execute_input":"2025-10-22T11:25:18.300562Z","iopub.status.idle":"2025-10-22T11:25:18.483654Z","shell.execute_reply.started":"2025-10-22T11:25:18.300545Z","shell.execute_reply":"2025-10-22T11:25:18.482844Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Компилируем модель\nmodel.compile(\n    optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),\n    loss='sparse_categorical_crossentropy',\n    metrics=['accuracy']\n)\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:25:18.486447Z","iopub.execute_input":"2025-10-22T11:25:18.486693Z","iopub.status.idle":"2025-10-22T11:25:18.527161Z","shell.execute_reply.started":"2025-10-22T11:25:18.486676Z","shell.execute_reply":"2025-10-22T11:25:18.526429Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Callbacks\nearly_stopping = EarlyStopping(\n    monitor='val_loss', patience=15, restore_best_weights=True, verbose=1\n)\n\nreduce_lr = ReduceLROnPlateau(\n    monitor='val_loss', factor=0.5, patience=5, min_lr=1e-7, verbose=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:25:18.527952Z","iopub.execute_input":"2025-10-22T11:25:18.528447Z","iopub.status.idle":"2025-10-22T11:25:18.532071Z","shell.execute_reply.started":"2025-10-22T11:25:18.528423Z","shell.execute_reply":"2025-10-22T11:25:18.531496Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Обучаем модель\nprint(\"Начало обучения модели...\")\nhistory = model.fit(\n    [X_train_spec, X_train_feat], y_train,\n    epochs=100,\n    batch_size=32,\n    validation_data=([X_val_spec, X_val_feat], y_val),\n    callbacks=[early_stopping, reduce_lr],\n    verbose=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:25:18.532788Z","iopub.execute_input":"2025-10-22T11:25:18.533172Z","iopub.status.idle":"2025-10-22T11:30:26.295996Z","shell.execute_reply.started":"2025-10-22T11:25:18.533154Z","shell.execute_reply":"2025-10-22T11:30:26.295414Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Визуализируем процесс обучения\nplt.figure(figsize=(15, 5))\n\nplt.subplot(1, 2, 1)\nplt.plot(history.history['accuracy'], label='Training Accuracy')\nplt.plot(history.history['val_accuracy'], label='Validation Accuracy')\nplt.title('Точность модели')\nplt.xlabel('Эпоха')\nplt.ylabel('Точность')\nplt.legend()\n\nplt.subplot(1, 2, 2)\nplt.plot(history.history['loss'], label='Training Loss')\nplt.plot(history.history['val_loss'], label='Validation Loss')\nplt.title('Функция потерь')\nplt.xlabel('Эпоха')\nplt.ylabel('Потери')\nplt.legend()\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:30:26.296888Z","iopub.execute_input":"2025-10-22T11:30:26.297085Z","iopub.status.idle":"2025-10-22T11:30:26.669134Z","shell.execute_reply.started":"2025-10-22T11:30:26.297070Z","shell.execute_reply":"2025-10-22T11:30:26.668405Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Оценка модели\nval_loss, val_accuracy = model.evaluate([X_val_spec, X_val_feat], y_val, verbose=0)\nprint(f\"Валидационная точность: {val_accuracy:.4f}\")\nprint(f\"Валидационные потери: {val_loss:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:30:26.670035Z","iopub.execute_input":"2025-10-22T11:30:26.670311Z","iopub.status.idle":"2025-10-22T11:30:27.327988Z","shell.execute_reply.started":"2025-10-22T11:30:26.670288Z","shell.execute_reply":"2025-10-22T11:30:27.327020Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Предсказания и матрица ошибок\ny_pred = model.predict([X_val_spec, X_val_feat])\ny_pred_classes = np.argmax(y_pred, axis=1)\n\nplt.figure(figsize=(12, 10))\ncm = confusion_matrix(y_val, y_pred_classes)\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues', \n            xticklabels=label_encoder.classes_,\n            yticklabels=label_encoder.classes_)\nplt.title('Матрица ошибок CNN модели')\nplt.xlabel('Предсказанные метки')\nplt.ylabel('Истинные метки')\nplt.xticks(rotation=90)\nplt.yticks(rotation=0)\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:30:27.328946Z","iopub.execute_input":"2025-10-22T11:30:27.329234Z","iopub.status.idle":"2025-10-22T11:30:31.711890Z","shell.execute_reply.started":"2025-10-22T11:30:27.329211Z","shell.execute_reply":"2025-10-22T11:30:31.710961Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Функция для предсказания на тестовых данных\ndef predict_test_data(model, test_audio_path, sample_submission_df, label_encoder):\n    print(\"Обработка тестовых данных...\")\n    test_spectrograms = []\n    test_features = []\n    test_filenames = []\n    \n    for fname in sample_submission_df['fname']:\n        file_path = os.path.join(test_audio_path, fname)\n        result = extract_comprehensive_features(file_path)\n        \n        if result is not None:\n            spectrogram = np.expand_dims(result['mel_spectrogram'], axis=-1)\n            test_spectrograms.append(spectrogram)\n            test_features.append(result['features'])\n            test_filenames.append(fname)\n    \n    X_test_spec = np.array(test_spectrograms)\n    X_test_feat = np.array(test_features)\n    \n    # Предсказания\n    test_predictions = model.predict([X_test_spec, X_test_feat])\n    test_pred_classes = np.argmax(test_predictions, axis=1)\n    test_pred_labels = label_encoder.inverse_transform(test_pred_classes)\n    \n    # Создаем submission файл\n    submission_df = pd.DataFrame({\n        'fname': test_filenames,\n        'label': test_pred_labels\n    })\n    \n    return submission_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:30:31.714612Z","iopub.execute_input":"2025-10-22T11:30:31.714834Z","iopub.status.idle":"2025-10-22T11:30:31.720536Z","shell.execute_reply.started":"2025-10-22T11:30:31.714816Z","shell.execute_reply":"2025-10-22T11:30:31.719836Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Предсказываем на тестовых данных\nfinal_submission = predict_test_data(model, test_audio_path, sample_submission, label_encoder)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T11:35:17.409716Z","iopub.execute_input":"2025-10-22T11:35:17.410207Z","iopub.status.idle":"2025-10-22T11:57:13.968082Z","shell.execute_reply.started":"2025-10-22T11:35:17.410184Z","shell.execute_reply":"2025-10-22T11:57:13.967491Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Сохраняем результаты\nfinal_submission.to_csv('submission.csv', index=False)\nprint(\"Submission файл сохранен как 'submission.csv'\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T12:05:55.150184Z","iopub.execute_input":"2025-10-22T12:05:55.150824Z","iopub.status.idle":"2025-10-22T12:05:55.170493Z","shell.execute_reply.started":"2025-10-22T12:05:55.150801Z","shell.execute_reply":"2025-10-22T12:05:55.169717Z"}},"outputs":[],"execution_count":null}]}