{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":7634,"databundleVersionId":46676,"sourceType":"competition"},{"sourceId":804403,"sourceType":"datasetVersion","datasetId":421323},{"sourceId":13685055,"sourceType":"datasetVersion","datasetId":8703249}],"dockerImageVersionId":12836,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\n\narchive_path = '../input/tensorflow-speech-recognition-challenge/train.7z'\noutput_dir = \"/kaggle/working\"\n# Create the output directory if it doesn't exist\nos.makedirs(output_dir, exist_ok=True)\n\nprint(f\"Attempting extraction using system 7z utility...\")\n\n!7z x {archive_path} -o{output_dir} -mmt=on -y > /dev/null 2>&1\n\nprint(\"\\n--- 7z Command Output Above ---\")\nprint(\"Check the specified output directory for files.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T15:51:29.548259Z","iopub.execute_input":"2025-11-11T15:51:29.548568Z","iopub.status.idle":"2025-11-11T15:53:45.386072Z","shell.execute_reply.started":"2025-11-11T15:51:29.548516Z","shell.execute_reply":"2025-11-11T15:53:45.385092Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nimport librosa\nimport tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras import layers, models\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import LabelEncoder\nimport matplotlib.pyplot as plt\nimport pickle\nfrom collections import Counter, defaultdict\nimport seaborn as sns\nfrom tensorflow.keras.metrics import top_k_categorical_accuracy\n\n\n# ==================== CONFIGURATION ====================\nSAMPLE_RATE = 16000\nDURATION = 1.0\nSAMPLES_PER_AUDIO = int(SAMPLE_RATE * DURATION)\n\nN_MFCC = 40\nHOP_LENGTH = 512\n\n# Training parameters - Optimized for large speaker set\nBATCH_SIZE = 128  # Larger batch for 2000+ speakers\nEPOCHS = 100\nLEARNING_RATE = 0.001\nMIN_SAMPLES_PER_SPEAKER = 12  # Increased for better quality\n\n\n# ==================== CRITICAL FIX: SPEAKER ID EXTRACTION ====================\n\ndef extract_speaker_id(filename):\n    name = filename.replace('.wav', '')\n    # Extract only the speaker hash (first part before _nohash)\n    speaker_base = name.split('_')[0]\n    return speaker_base\n\n\n# ==================== DATA LOADING WITH VERIFICATION ====================\n\ndef load_audio(filepath, sr=SAMPLE_RATE, duration=DURATION):\n    \"\"\"Load and preprocess audio file\"\"\"\n    try:\n        audio, _ = librosa.load(filepath, sr=sr, duration=duration)\n        if len(audio) < SAMPLES_PER_AUDIO:\n            audio = np.pad(audio, (0, SAMPLES_PER_AUDIO - len(audio)), mode='constant')\n        else:\n            audio = audio[:SAMPLES_PER_AUDIO]\n        return audio\n    except Exception as e:\n        print(f\"Error loading {filepath}: {e}\")\n        return None\n\n\ndef extract_mfcc_features(audio, sr=SAMPLE_RATE, n_mfcc=N_MFCC):\n    \"\"\"Extract MFCC features with deltas\"\"\"\n    mfcc = librosa.feature.mfcc(y=audio, sr=sr, n_mfcc=n_mfcc, hop_length=HOP_LENGTH)\n    mfcc_delta = librosa.feature.delta(mfcc)\n    mfcc_delta2 = librosa.feature.delta(mfcc, order=2)\n    features = np.concatenate([mfcc, mfcc_delta, mfcc_delta2], axis=0)\n    return features.T\n\n\ndef load_speaker_dataset(data_path, min_samples=MIN_SAMPLES_PER_SPEAKER):\n    \"\"\"Load dataset with CORRECT speaker grouping\"\"\"\n    \n    print(f\"\\n{'='*80}\")\n    print(\" \"*25 + \"LOADING DATASET\")\n    print(f\"{'='*80}\")\n    \n    speaker_files = {}\n    total_files = 0\n    \n    # Get word folders\n    word_folders = [f for f in os.listdir(data_path) \n                   if os.path.isdir(os.path.join(data_path, f))\n                   and f != '_background_noise_']\n    \n    print(f\"\\nScanning {len(word_folders)} word folders...\")\n    \n    # Scan all files\n    for word in word_folders:\n        folder_path = os.path.join(data_path, word)\n        for filename in os.listdir(folder_path):\n            if filename.endswith('.wav'):\n                # ✅ Extract BASE speaker ID (groups recordings from same person)\n                speaker_id = extract_speaker_id(filename)\n                filepath = os.path.join(folder_path, filename)\n                \n                if speaker_id not in speaker_files:\n                    speaker_files[speaker_id] = []\n                speaker_files[speaker_id].append(filepath)\n                total_files += 1\n    \n    print(f\"✓ Found {total_files} audio files\")\n    print(f\"✓ Found {len(speaker_files)} UNIQUE speakers (before filtering)\")\n    \n    # Show grouping verification\n    print(f\"\\n✅ SPEAKER GROUPING VERIFICATION:\")\n    sample_speaker = list(speaker_files.keys())[0]\n    sample_files = speaker_files[sample_speaker][:3]\n    print(f\"  Example speaker: {sample_speaker}\")\n    print(f\"  Their files:\")\n    for f in sample_files:\n        print(f\"    - {os.path.basename(f)}\")\n    \n    # Filter speakers\n    valid_speakers = {spk: files for spk, files in speaker_files.items() \n                     if len(files) >= min_samples}\n    \n    removed = len(speaker_files) - len(valid_speakers)\n    \n    print(f\"\\n📊 FILTERING RESULTS:\")\n    print(f\"  Minimum samples required: {min_samples}\")\n    print(f\"  Speakers kept: {len(valid_speakers)}\")\n    print(f\"  Speakers removed: {removed}\")\n    print(f\"  Total training samples: {sum(len(f) for f in valid_speakers.values())}\")\n    \n    # Statistics\n    counts = [len(files) for files in valid_speakers.values()]\n    print(f\"\\n📈 SAMPLES PER SPEAKER:\")\n    print(f\"  Min: {min(counts)}\")\n    print(f\"  Max: {max(counts)}\")\n    print(f\"  Mean: {np.mean(counts):.1f}\")\n    print(f\"  Median: {np.median(counts):.0f}\")\n    \n    # Top speakers\n    top = sorted(valid_speakers.items(), key=lambda x: len(x[1]), reverse=True)[:5]\n    print(f\"\\n🏆 TOP 5 SPEAKERS:\")\n    for spk, files in top:\n        print(f\"  {spk}: {len(files)} samples\")\n    \n    print(f\"{'='*80}\\n\")\n    \n    return valid_speakers\n\n\ndef extract_features_from_dataset(speaker_files):\n    \"\"\"Extract features from all audio files\"\"\"\n    \n    print(f\"\\n{'='*80}\")\n    print(\" \"*25 + \"EXTRACTING FEATURES\")\n    print(f\"{'='*80}\\n\")\n    \n    all_features = []\n    all_labels = []\n    total = sum(len(f) for f in speaker_files.values())\n    processed = 0\n    errors = 0\n    \n    for speaker_id, filepaths in speaker_files.items():\n        for filepath in filepaths:\n            processed += 1\n            \n            if processed % 1000 == 0:\n                print(f\"Progress: {processed}/{total} ({processed/total*100:.1f}%) - Errors: {errors}\")\n            \n            audio = load_audio(filepath)\n            if audio is None:\n                errors += 1\n                continue\n            \n            try:\n                features = extract_mfcc_features(audio)\n                all_features.append(features)\n                all_labels.append(speaker_id)\n            except Exception as e:\n                errors += 1\n                if errors <= 5:\n                    print(f\"  Error: {filepath}: {e}\")\n    \n    print(f\"\\n✓ Successfully processed: {len(all_features)}\")\n    print(f\"✗ Errors: {errors}\")\n    \n    # Convert to arrays\n    X = np.array(all_features)\n    \n    # Encode labels\n    label_encoder = LabelEncoder()\n    y_encoded = label_encoder.fit_transform(all_labels)\n    y_categorical = keras.utils.to_categorical(y_encoded)\n    \n    print(f\"\\n{'='*80}\")\n    print(\" \"*28 + \"DATASET SUMMARY\")\n    print(f\"{'='*80}\")\n    print(f\"\\nFeatures shape: {X.shape}\")\n    print(f\"  Samples: {X.shape[0]}\")\n    print(f\"  Time steps: {X.shape[1]}\")\n    print(f\"  Features: {X.shape[2]}\")\n    print(f\"\\nNumber of speakers: {len(label_encoder.classes_)}\")\n    print(f\"Labels shape: {y_categorical.shape}\")\n    \n    # ✅ VERIFICATION: Show sample speaker IDs\n    print(f\"\\n✅ SAMPLE SPEAKER IDs (first 10):\")\n    for i, spk in enumerate(label_encoder.classes_[:10]):\n        print(f\"  {i+1}. {spk}\")\n    \n    print(f\"{'='*80}\\n\")\n    \n    return X, y_categorical, label_encoder\n\n\n# ==================== IMPROVED MODEL ====================\n\ndef build_speaker_model(input_shape, num_speakers):\n    \"\"\"Build improved model for large-scale speaker recognition\"\"\"\n    from tensorflow.keras import regularizers\n    \n    model = models.Sequential()\n    \n    # Block 1\n    model.add(layers.Conv1D(128, 3, padding='same', activation='relu', input_shape=input_shape))\n    model.add(layers.BatchNormalization())\n    model.add(layers.Conv1D(128, 3, padding='same', activation='relu'))\n    model.add(layers.BatchNormalization())\n    model.add(layers.MaxPooling1D(2))\n    model.add(layers.Dropout(0.3))\n    \n    # Block 2\n    model.add(layers.Conv1D(256, 3, padding='same', activation='relu'))\n    model.add(layers.BatchNormalization())\n    model.add(layers.Conv1D(256, 3, padding='same', activation='relu'))\n    model.add(layers.BatchNormalization())\n    model.add(layers.MaxPooling1D(2))\n    model.add(layers.Dropout(0.4))\n    \n    # Block 3\n    model.add(layers.Conv1D(512, 3, padding='same', activation='relu'))\n    model.add(layers.BatchNormalization())\n    model.add(layers.GlobalAveragePooling1D())\n    model.add(layers.Dropout(0.5))\n    \n    # Dense layers\n    model.add(layers.Dense(256, activation='relu', kernel_regularizer=regularizers.l2(0.001)))\n    model.add(layers.BatchNormalization())\n    model.add(layers.Dropout(0.5))\n    \n    model.add(layers.Dense(128, activation='relu', kernel_regularizer=regularizers.l2(0.001)))\n    model.add(layers.Dropout(0.5))\n    \n    # Output\n    model.add(layers.Dense(num_speakers, activation='softmax'))\n    \n    return model\n\n\n# ==================== TRAINING WITH TOP-K ACCURACY ====================\n\ndef train_speaker_model(model, X_train, y_train, X_val, y_val, epochs=EPOCHS):\n    \"\"\"Train model with Top-K accuracy tracking (backward compatible)\"\"\"\n    from tensorflow.keras.metrics import top_k_categorical_accuracy\n\n    # Define callbacks safely\n    callbacks = [\n        keras.callbacks.ReduceLROnPlateau(\n            monitor='val_loss', factor=0.5, patience=5, min_lr=1e-7, verbose=1\n        ),\n        # 🧠 Older Keras doesn’t support restore_best_weights\n        keras.callbacks.EarlyStopping(\n            monitor='val_loss',\n            patience=15,\n            verbose=1\n        ),\n        keras.callbacks.ModelCheckpoint(\n            'best_speaker_model.h5',\n            monitor='val_accuracy',  # Safer for compatibility\n            save_best_only=True,\n            verbose=1\n        )\n    ]\n\n    # Compile model with backward compatibility for optimizer & metrics\n    try:\n        model.compile(\n            optimizer=keras.optimizers.Adam(learning_rate=LEARNING_RATE),\n            loss='categorical_crossentropy',\n            metrics=[\n                'accuracy',\n                lambda y_true, y_pred: top_k_categorical_accuracy(y_true, y_pred, k=5),\n                lambda y_true, y_pred: top_k_categorical_accuracy(y_true, y_pred, k=10)\n            ]\n        )\n    except TypeError:\n        model.compile(\n            optimizer=keras.optimizers.Adam(lr=LEARNING_RATE),\n            loss='categorical_crossentropy',\n            metrics=['accuracy']\n        )\n\n    print(f\"\\n{'='*80}\")\n    print(\" \"*30 + \"TRAINING STARTED\")\n    print(f\"{'='*80}\\n\")\n\n    # Train model\n    history = model.fit(\n        X_train, y_train,\n        validation_data=(X_val, y_val),\n        batch_size=BATCH_SIZE,\n        epochs=epochs,\n        callbacks=callbacks,\n        verbose=1\n    )\n\n    # ✅ Reload best weights manually (for older TF versions)\n    try:\n        model.load_weights('best_speaker_model.h5')\n        print(\"\\n[INFO] Best model weights restored from checkpoint.\")\n    except Exception as e:\n        print(f\"[WARN] Could not restore best weights: {e}\")\n\n    return history\n\n\n\n# ==================== VISUALIZATION ====================\n\ndef plot_training_history(history):\n    \"\"\"Plot training metrics\"\"\"\n    keys = history.history.keys()\n    acc_key = 'accuracy' if 'accuracy' in keys else 'acc'\n    val_acc_key = 'val_accuracy' if 'val_accuracy' in keys else 'val_acc'\n    \n    has_top5 = 'top_5_accuracy' in keys\n    \n    if has_top5:\n        fig, axes = plt.subplots(2, 2, figsize=(16, 10))\n        \n        # Top-1 Accuracy\n        axes[0, 0].plot(history.history[acc_key], label='Train', linewidth=2)\n        axes[0, 0].plot(history.history[val_acc_key], label='Val', linewidth=2)\n        axes[0, 0].set_title('Top-1 Accuracy', fontsize=14, fontweight='bold')\n        axes[0, 0].set_xlabel('Epoch')\n        axes[0, 0].set_ylabel('Accuracy')\n        axes[0, 0].legend()\n        axes[0, 0].grid(True, alpha=0.3)\n        \n        # Top-5 Accuracy\n        axes[0, 1].plot(history.history['top_5_accuracy'], label='Train', linewidth=2)\n        axes[0, 1].plot(history.history['val_top_5_accuracy'], label='Val', linewidth=2)\n        axes[0, 1].set_title('Top-5 Accuracy', fontsize=14, fontweight='bold')\n        axes[0, 1].set_xlabel('Epoch')\n        axes[0, 1].set_ylabel('Accuracy')\n        axes[0, 1].legend()\n        axes[0, 1].grid(True, alpha=0.3)\n        \n        # Loss\n        axes[1, 0].plot(history.history['loss'], label='Train', linewidth=2)\n        axes[1, 0].plot(history.history['val_loss'], label='Val', linewidth=2)\n        axes[1, 0].set_title('Loss', fontsize=14, fontweight='bold')\n        axes[1, 0].set_xlabel('Epoch')\n        axes[1, 0].set_ylabel('Loss')\n        axes[1, 0].legend()\n        axes[1, 0].grid(True, alpha=0.3)\n        \n        # Overfitting Gap\n        axes[1, 1].plot(\n            np.array(history.history[acc_key]) - np.array(history.history[val_acc_key]),\n            linewidth=2, color='red'\n        )\n        axes[1, 1].axhline(0, color='gray', linestyle='--')\n        axes[1, 1].set_title('Overfitting Gap', fontsize=14, fontweight='bold')\n        axes[1, 1].set_xlabel('Epoch')\n        axes[1, 1].set_ylabel('Train - Val Accuracy')\n        axes[1, 1].grid(True, alpha=0.3)\n    else:\n        fig, axes = plt.subplots(1, 2, figsize=(14, 5))\n        axes[0].plot(history.history[acc_key], label='Train', linewidth=2)\n        axes[0].plot(history.history[val_acc_key], label='Val', linewidth=2)\n        axes[0].set_title('Accuracy')\n        axes[0].legend()\n        axes[0].grid(True, alpha=0.3)\n        \n        axes[1].plot(history.history['loss'], label='Train', linewidth=2)\n        axes[1].plot(history.history['val_loss'], label='Val', linewidth=2)\n        axes[1].set_title('Loss')\n        axes[1].legend()\n        axes[1].grid(True, alpha=0.3)\n    \n    plt.tight_layout()\n    plt.savefig('training_history.png', dpi=150, bbox_inches='tight')\n    plt.show()\n    print(\"✓ Saved: training_history.png\")\n\n\n# ==================== EVALUATION & ANALYSIS ====================\n\ndef evaluate_model(model, X_test, y_test, label_encoder):\n    \"\"\"Comprehensive evaluation\"\"\"\n    \n    print(f\"\\n{'='*80}\")\n    print(\" \"*30 + \"EVALUATION\")\n    print(f\"{'='*80}\\n\")\n    \n    y_pred_probs = model.predict(X_test, verbose=0)\n    y_pred = np.argmax(y_pred_probs, axis=1)\n    y_true = np.argmax(y_test, axis=1)\n    \n    # Top-K accuracy\n    print(\"🎯 ACCURACY METRICS:\")\n    for k in [1, 3, 5, 10, 20]:\n        top_k = np.argsort(y_pred_probs, axis=1)[:, -k:]\n        acc = np.mean([y_true[i] in top_k[i] for i in range(len(y_true))])\n        print(f\"  Top-{k:2d}: {acc:.4f} ({acc*100:.2f}%)\")\n    \n    # Confidence analysis\n    max_conf = np.max(y_pred_probs, axis=1)\n    correct = (y_pred == y_true)\n    \n    print(f\"\\n🔍 CONFIDENCE:\")\n    print(f\"  All: {np.mean(max_conf):.3f}\")\n    print(f\"  Correct: {np.mean(max_conf[correct]):.3f}\")\n    print(f\"  Incorrect: {np.mean(max_conf[~correct]):.3f}\")\n    \n    # Per-speaker stats\n    speaker_acc = {}\n    for i in range(len(y_true)):\n        spk = label_encoder.inverse_transform([y_true[i]])[0]\n        if spk not in speaker_acc:\n            speaker_acc[spk] = {'correct': 0, 'total': 0}\n        speaker_acc[spk]['total'] += 1\n        if y_pred[i] == y_true[i]:\n            speaker_acc[spk]['correct'] += 1\n    \n    accs = [(s, d['correct']/d['total']) for s, d in speaker_acc.items()]\n    \n    print(f\"\\n🏆 BEST SPEAKERS:\")\n    for spk, acc in sorted(accs, key=lambda x: x[1], reverse=True)[:5]:\n        print(f\"  {spk}: {acc:.2%}\")\n    \n    print(f\"\\n⚠️ WORST SPEAKERS:\")\n    for spk, acc in sorted(accs, key=lambda x: x[1])[:5]:\n        print(f\"  {spk}: {acc:.2%}\")\n    \n    print(f\"{'='*80}\\n\")\n    \n    return np.mean(correct)\n\n\n# ==================== PREDICTION ====================\n\ndef predict_speaker(model, audio_file, label_encoder, top_k=5):\n    \"\"\"Predict speaker with proper feature extraction\"\"\"\n    \n    print(f\"\\n{'='*80}\")\n    print(f\"PREDICTING: {os.path.basename(audio_file)}\")\n    print(f\"{'='*80}\\n\")\n    \n    # Expected speaker from filename\n    expected = extract_speaker_id(os.path.basename(audio_file))\n    print(f\"Expected speaker: {expected}\")\n    \n    # Check if in training set\n    if expected in label_encoder.classes_:\n        print(f\"✓ Speaker IS in training set\")\n    else:\n        print(f\"✗ Speaker NOT in training set - prediction will be wrong!\")\n    \n    # Load and predict\n    audio = load_audio(audio_file)\n    if audio is None:\n        return None, None\n    \n    features = extract_mfcc_features(audio)\n    features = np.expand_dims(features, axis=0)\n    \n    probs = model.predict(features, verbose=0)[0]\n    \n    # Top K\n    top_idx = np.argsort(probs)[-top_k:][::-1]\n    top_spk = label_encoder.inverse_transform(top_idx)\n    top_conf = probs[top_idx]\n    \n    print(f\"\\nTop {top_k} Predictions:\")\n    print(\"-\" * 80)\n    \n    for i, (spk, conf) in enumerate(zip(top_spk, top_conf), 1):\n        bar = \"█\" * int(conf * 40) + \"░\" * (40 - int(conf * 40))\n        marker = \"✓\" if spk == expected else \" \"\n        print(f\"{marker} {i}. {spk:25s} {conf:6.2%} │{bar}│\")\n    \n    print(\"-\" * 80)\n    print(f\"\\n✓ PREDICTED: {top_spk[0]} (confidence: {top_conf[0]:.2%})\")\n    \n    if top_spk[0] == expected:\n        print(f\"✓ CORRECT PREDICTION!\")\n    else:\n        print(f\"✗ WRONG - Expected: {expected}\")\n    \n    print(f\"{'='*80}\\n\")\n    \n    return top_spk[0], top_conf[0]\n\n\n# ==================== SAVE/LOAD ====================\n\ndef save_model_and_encoder(model, label_encoder, model_path='speaker_model.h5', \n                           encoder_path='label_encoder.pkl'):\n    \"\"\"Save model and encoder\"\"\"\n    model.save(model_path)\n    with open(encoder_path, 'wb') as f:\n        pickle.dump(label_encoder, f)\n    print(f\"\\n✓ Saved: {model_path}\")\n    print(f\"✓ Saved: {encoder_path}\")\n\n\ndef load_model_and_encoder(model_path='speaker_model.h5', \n                           encoder_path='label_encoder.pkl'):\n    \"\"\"Load model and encoder\"\"\"\n    model = keras.models.load_model(model_path)\n    with open(encoder_path, 'rb') as f:\n        label_encoder = pickle.load(f)\n    print(f\"✓ Loaded: {model_path}\")\n    print(f\"✓ Loaded: {encoder_path}\")\n    print(f\"✓ Recognizes {len(label_encoder.classes_)} speakers\")\n    return model, label_encoder","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T15:53:45.388567Z","iopub.execute_input":"2025-11-11T15:53:45.388832Z","iopub.status.idle":"2025-11-11T15:53:47.193567Z","shell.execute_reply.started":"2025-11-11T15:53:45.388780Z","shell.execute_reply":"2025-11-11T15:53:47.192702Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"    print(\"\\n\" + \"=\"*80)\n    print(\" \"*20 + \"SPEAKER RECOGNITION SYSTEM\")\n    print(\"=\"*80)\n    \n    DATA_PATH = '/kaggle/working/train/audio/'\n# Load\n    print(\"\\n[1/6] Loading dataset...\")\n    speaker_files = load_speaker_dataset(DATA_PATH, MIN_SAMPLES_PER_SPEAKER)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T15:53:47.196014Z","iopub.execute_input":"2025-11-11T15:53:47.196334Z","iopub.status.idle":"2025-11-11T15:53:47.384282Z","shell.execute_reply.started":"2025-11-11T15:53:47.196272Z","shell.execute_reply":"2025-11-11T15:53:47.383415Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"   # Extract features\n    print(\"\\n[2/6] Extracting features...\")\n    X, y, label_encoder = extract_features_from_dataset(speaker_files)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T15:53:47.389029Z","iopub.execute_input":"2025-11-11T15:53:47.389325Z","iopub.status.idle":"2025-11-11T16:06:39.616810Z","shell.execute_reply.started":"2025-11-11T15:53:47.389271Z","shell.execute_reply":"2025-11-11T16:06:39.615219Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import warnings\nwarnings.filterwarnings(\"ignore\", category=DeprecationWarning)\n\n# Split\nprint(\"\\n[3/6] Splitting data...\")\nX_train, X_val, y_train, y_val = train_test_split(\n        X, y, test_size=0.2, random_state=42, stratify=y.argmax(axis=1)\n    )\nprint(f\"Train: {len(X_train)}, Val: {len(X_val)}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T16:06:39.618199Z","iopub.execute_input":"2025-11-11T16:06:39.618397Z","iopub.status.idle":"2025-11-11T16:06:41.456001Z","shell.execute_reply.started":"2025-11-11T16:06:39.618362Z","shell.execute_reply":"2025-11-11T16:06:41.455212Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"    # Build\n    print(\"\\n[4/6] Building model...\")\n    model = build_speaker_model((X.shape[1], X.shape[2]), y.shape[1])\n    model.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T16:06:41.456954Z","iopub.execute_input":"2025-11-11T16:06:41.457146Z","iopub.status.idle":"2025-11-11T16:06:42.066642Z","shell.execute_reply.started":"2025-11-11T16:06:41.457112Z","shell.execute_reply":"2025-11-11T16:06:42.065947Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"    # Train\n    print(\"\\n[5/6] Training...\")\n    history = train_speaker_model(model, X_train, y_train, X_val, y_val, EPOCHS)\n    ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T16:06:42.067479Z","iopub.execute_input":"2025-11-11T16:06:42.067671Z","iopub.status.idle":"2025-11-11T16:16:49.068284Z","shell.execute_reply.started":"2025-11-11T16:06:42.067636Z","shell.execute_reply":"2025-11-11T16:16:49.067577Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"   # Plot\n    plot_training_history(history)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T16:16:49.069368Z","iopub.execute_input":"2025-11-11T16:16:49.069678Z","iopub.status.idle":"2025-11-11T16:16:50.361665Z","shell.execute_reply.started":"2025-11-11T16:16:49.069625Z","shell.execute_reply":"2025-11-11T16:16:50.360367Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"    # Evaluate\n    print(\"\\n[6/6] Evaluating...\")\n    acc = evaluate_model(model, X_val, y_val, label_encoder)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T16:16:50.363045Z","iopub.execute_input":"2025-11-11T16:16:50.363327Z","iopub.status.idle":"2025-11-11T16:16:59.297163Z","shell.execute_reply.started":"2025-11-11T16:16:50.363276Z","shell.execute_reply":"2025-11-11T16:16:59.296177Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"    # Save\nsave_model_and_encoder(model, label_encoder)\n    \n    print(\"\\n\" + \"=\"*80)\n    print(\" \"*25 + \"TRAINING COMPLETE!\")\n    print(f\" \"*20 + f\"Validation Accuracy: {acc:.2%}\")\n    print(\"=\"*80 + \"\\n\")\n    \n # model, label_encoder, speaker_files","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T16:16:59.298232Z","iopub.execute_input":"2025-11-11T16:16:59.298497Z","iopub.status.idle":"2025-11-11T16:16:59.771980Z","shell.execute_reply.started":"2025-11-11T16:16:59.298431Z","shell.execute_reply":"2025-11-11T16:16:59.771224Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nimport librosa\nimport librosa.display\nimport matplotlib.pyplot as plt\nimport IPython.display as ipd\nfrom glob import glob\n\n\n# ==================== AUDIO PLAYBACK ====================\n\ndef play_audio(audio_path, sr=16000):\n    \"\"\"\n    Play audio file in Jupyter/Kaggle notebook\n    \n    Args:\n        audio_path: Path to audio file\n        sr: Sample rate (default: 16000)\n    \n    Returns:\n        Audio widget for playback\n    \"\"\"\n    if not os.path.exists(audio_path):\n        print(f\"❌ File not found: {audio_path}\")\n        return None\n    \n    try:\n        # Load audio\n        audio, sample_rate = librosa.load(audio_path, sr=sr)\n        \n        print(f\"🎵 Playing: {os.path.basename(audio_path)}\")\n        print(f\"   Duration: {len(audio)/sample_rate:.2f}s\")\n        print(f\"   Sample rate: {sample_rate} Hz\\n\")\n        \n        # Create audio widget\n        return ipd.Audio(audio, rate=sample_rate)\n    \n    except Exception as e:\n        print(f\"❌ Error loading audio: {e}\")\n        return None\n\n\ndef visualize_audio(audio_path, sr=16000):\n    \"\"\"\n    Visualize audio waveform and spectrogram\n    \n    Args:\n        audio_path: Path to audio file\n        sr: Sample rate\n    \"\"\"\n    if not os.path.exists(audio_path):\n        print(f\"❌ File not found: {audio_path}\")\n        return\n    \n    try:\n        # Load audio\n        audio, sample_rate = librosa.load(audio_path, sr=sr)\n        \n        # Create figure\n        fig, axes = plt.subplots(3, 1, figsize=(14, 10))\n        \n        # 1. Waveform\n        axes[0].plot(np.linspace(0, len(audio)/sample_rate, len(audio)), audio, linewidth=0.5)\n        axes[0].set_title(f'Waveform: {os.path.basename(audio_path)}', fontsize=14, fontweight='bold')\n        axes[0].set_xlabel('Time (s)')\n        axes[0].set_ylabel('Amplitude')\n        axes[0].grid(True, alpha=0.3)\n        \n        # 2. Spectrogram\n        D = librosa.amplitude_to_db(np.abs(librosa.stft(audio)), ref=np.max)\n        img = librosa.display.specshow(D, sr=sample_rate, x_axis='time', y_axis='hz', ax=axes[1])\n        axes[1].set_title('Spectrogram (Frequency over Time)', fontsize=14, fontweight='bold')\n        fig.colorbar(img, ax=axes[1], format='%+2.0f dB')\n        \n        # 3. Mel Spectrogram (what the model sees)\n        mel_spec = librosa.feature.melspectrogram(y=audio, sr=sample_rate, n_mels=128)\n        mel_spec_db = librosa.power_to_db(mel_spec, ref=np.max)\n        img2 = librosa.display.specshow(mel_spec_db, sr=sample_rate, x_axis='time', y_axis='mel', ax=axes[2])\n        axes[2].set_title('Mel Spectrogram (Model Input)', fontsize=14, fontweight='bold')\n        fig.colorbar(img2, ax=axes[2], format='%+2.0f dB')\n        \n        plt.tight_layout()\n        plt.savefig('audio_visualization.png', dpi=150, bbox_inches='tight')\n        plt.show()\n        \n        print(\"✓ Visualization saved as 'audio_visualization.png'\\n\")\n        \n    except Exception as e:\n        print(f\"❌ Error visualizing audio: {e}\")\n\n\n# ==================== FIND SIMILAR SAMPLES ====================\n\ndef find_speaker_samples(speaker_id, data_path='/kaggle/working/train/audio/', \n                         max_samples=5, random_sample=True):\n    \"\"\"\n    Find audio samples for a specific speaker\n    \n    Args:\n        speaker_id: Speaker ID to find (e.g., 'ec201020')\n        data_path: Path to audio data directory\n        max_samples: Maximum number of samples to return\n        random_sample: If True, randomly sample files; if False, return first N\n    \n    Returns:\n        List of file paths\n    \"\"\"\n    print(f\"\\n🔍 Searching for samples of speaker: {speaker_id}\")\n    \n    # Search all word folders\n    all_files = []\n    word_folders = [f for f in os.listdir(data_path) \n                   if os.path.isdir(os.path.join(data_path, f))\n                   and f != '_background_noise_']\n    \n    for word in word_folders:\n        folder_path = os.path.join(data_path, word)\n        pattern = os.path.join(folder_path, f\"{speaker_id}_*.wav\")\n        files = glob(pattern)\n        all_files.extend(files)\n    \n    if not all_files:\n        print(f\"❌ No samples found for speaker: {speaker_id}\")\n        return []\n    \n    print(f\"✓ Found {len(all_files)} samples\")\n    \n    # Sample files\n    if random_sample and len(all_files) > max_samples:\n        selected = np.random.choice(all_files, max_samples, replace=False).tolist()\n    else:\n        selected = all_files[:max_samples]\n    \n    print(f\"✓ Selected {len(selected)} samples:\\n\")\n    for i, f in enumerate(selected, 1):\n        word = os.path.basename(os.path.dirname(f))\n        filename = os.path.basename(f)\n        print(f\"   {i}. [{word}] {filename}\")\n    \n    return selected\n\n\n# ==================== COMPARE AUDIO SAMPLES ====================\n\ndef compare_audio_samples(test_audio, predicted_speaker_id, \n                         data_path='/kaggle/working/train/audio/',\n                         num_comparisons=3, sr=16000):\n    \"\"\"\n    Compare test audio with samples from predicted speaker\n    \n    Args:\n        test_audio: Path to test audio file\n        predicted_speaker_id: Predicted speaker ID\n        data_path: Path to training data\n        num_comparisons: Number of comparison samples to show\n        sr: Sample rate\n    \"\"\"\n    print(\"\\n\" + \"=\"*80)\n    print(\" \"*20 + \"AUDIO COMPARISON\")\n    print(\"=\"*80 + \"\\n\")\n    \n    # Play test audio\n    print(\"🎧 TEST AUDIO:\")\n    print(f\"   File: {os.path.basename(test_audio)}\")\n    test_widget = play_audio(test_audio, sr)\n    if test_widget:\n        ipd.display(test_widget)\n    print()\n    \n    # Find samples of predicted speaker\n    comparison_files = find_speaker_samples(\n        predicted_speaker_id, \n        data_path, \n        max_samples=num_comparisons\n    )\n    \n    if not comparison_files:\n        print(\"\\n⚠️  No comparison samples available\")\n        return\n    \n    # Play comparison samples\n    print(f\"\\n🎧 PREDICTED SPEAKER SAMPLES (Speaker: {predicted_speaker_id}):\")\n    print(\"   Listen to these to verify if they sound similar:\\n\")\n    \n    for i, audio_file in enumerate(comparison_files, 1):\n        word = os.path.basename(os.path.dirname(audio_file))\n        filename = os.path.basename(audio_file)\n        \n        print(f\"   {i}. [{word}] {filename}\")\n        widget = play_audio(audio_file, sr)\n        if widget:\n            ipd.display(widget)\n        print()\n    \n    print(\"=\"*80 + \"\\n\")\n\n\n# ==================== SIDE-BY-SIDE COMPARISON ====================\n\ndef compare_spectrograms(audio1_path, audio2_path, \n                        label1=\"Test Audio\", label2=\"Predicted Speaker Sample\",\n                        sr=16000):\n    \"\"\"\n    Compare spectrograms of two audio files side-by-side\n    \n    Args:\n        audio1_path: Path to first audio file\n        audio2_path: Path to second audio file\n        label1: Label for first audio\n        label2: Label for second audio\n        sr: Sample rate\n    \"\"\"\n    print(\"\\n\" + \"=\"*80)\n    print(\" \"*20 + \"SPECTROGRAM COMPARISON\")\n    print(\"=\"*80 + \"\\n\")\n    \n    try:\n        # Load both audio files\n        audio1, sr1 = librosa.load(audio1_path, sr=sr)\n        audio2, sr2 = librosa.load(audio2_path, sr=sr)\n        \n        # Create figure\n        fig, axes = plt.subplots(2, 2, figsize=(16, 10))\n        \n        # Audio 1 - Waveform\n        axes[0, 0].plot(audio1, linewidth=0.5, color='blue')\n        axes[0, 0].set_title(f'{label1} - Waveform', fontsize=12, fontweight='bold')\n        axes[0, 0].set_ylabel('Amplitude')\n        axes[0, 0].grid(True, alpha=0.3)\n        \n        # Audio 1 - Mel Spectrogram\n        mel1 = librosa.feature.melspectrogram(y=audio1, sr=sr, n_mels=128)\n        mel1_db = librosa.power_to_db(mel1, ref=np.max)\n        img1 = librosa.display.specshow(mel1_db, sr=sr, x_axis='time', y_axis='mel', ax=axes[0, 1])\n        axes[0, 1].set_title(f'{label1} - Mel Spectrogram', fontsize=12, fontweight='bold')\n        fig.colorbar(img1, ax=axes[0, 1], format='%+2.0f dB')\n        \n        # Audio 2 - Waveform\n        axes[1, 0].plot(audio2, linewidth=0.5, color='green')\n        axes[1, 0].set_title(f'{label2} - Waveform', fontsize=12, fontweight='bold')\n        axes[1, 0].set_xlabel('Sample')\n        axes[1, 0].set_ylabel('Amplitude')\n        axes[1, 0].grid(True, alpha=0.3)\n        \n        # Audio 2 - Mel Spectrogram\n        mel2 = librosa.feature.melspectrogram(y=audio2, sr=sr, n_mels=128)\n        mel2_db = librosa.power_to_db(mel2, ref=np.max)\n        img2 = librosa.display.specshow(mel2_db, sr=sr, x_axis='time', y_axis='mel', ax=axes[1, 1])\n        axes[1, 1].set_title(f'{label2} - Mel Spectrogram', fontsize=12, fontweight='bold')\n        axes[1, 1].set_xlabel('Time (s)')\n        fig.colorbar(img2, ax=axes[1, 1], format='%+2.0f dB')\n        \n        plt.tight_layout()\n        plt.savefig('spectrogram_comparison.png', dpi=150, bbox_inches='tight')\n        plt.show()\n        \n        print(\"✓ Comparison saved as 'spectrogram_comparison.png'\\n\")\n        print(\"📊 Visual Comparison Tips:\")\n        print(\"   • Similar patterns in spectrograms suggest same speaker\")\n        print(\"   • Look for similar formant structure (bright horizontal bands)\")\n        print(\"   • Check pitch patterns and intensity distribution\")\n        print()\n        \n    except Exception as e:\n        print(f\"❌ Error comparing spectrograms: {e}\")\n\n\n# ==================== ALL-IN-ONE FUNCTION ====================\n\ndef predict_and_listen(test_audio_path, model, label_encoder,\n                      data_path='/kaggle/working/train/audio/',\n                      num_comparisons=3, show_spectrograms=True):\n    \"\"\"\n    Complete workflow: Predict, play audio, and compare with samples\n    \n    Args:\n        test_audio_path: Path to test audio file\n        model: Trained model\n        label_encoder: Label encoder\n        data_path: Path to training data\n        num_comparisons: Number of comparison samples\n        show_spectrograms: Whether to show spectrogram comparison\n    \"\"\"\n    # Import prediction function\n    from prediction_script import extract_mfcc_features, load_audio as load_audio_features\n    \n    print(\"\\n\" + \"🎵 \"*30)\n    print(\"PREDICT AND LISTEN - COMPLETE WORKFLOW\")\n    print(\"🎵 \"*30 + \"\\n\")\n    \n    # 1. Make prediction\n    print(\"=\"*80)\n    print(\"STEP 1: MAKING PREDICTION\")\n    print(\"=\"*80 + \"\\n\")\n    \n    audio = load_audio_features(test_audio_path)\n    if audio is None:\n        return\n    \n    features = extract_mfcc_features(audio)\n    features_batch = np.expand_dims(features, axis=0)\n    predictions = model.predict(features_batch, verbose=0)[0]\n    \n    # Get top 5 predictions\n    top_indices = np.argsort(predictions)[-5:][::-1]\n    top_speakers = label_encoder.inverse_transform(top_indices)\n    top_confidences = predictions[top_indices]\n    \n    print(f\"🎯 Top 5 Predictions:\")\n    for i, (speaker, conf) in enumerate(zip(top_speakers, top_confidences), 1):\n        bar = \"█\" * int(conf * 30) + \"░\" * (30 - int(conf * 30))\n        print(f\"   {i}. {speaker:15s} {conf:6.2%} │{bar}│\")\n    \n    predicted_speaker = top_speakers[0]\n    confidence = top_confidences[0]\n    \n    print(f\"\\n✓ Predicted Speaker: {predicted_speaker} ({confidence:.2%} confidence)\\n\")\n    \n    # 2. Play test audio\n    print(\"=\"*80)\n    print(\"STEP 2: LISTEN TO TEST AUDIO\")\n    print(\"=\"*80 + \"\\n\")\n    \n    print(\"🎧 YOUR TEST AUDIO:\")\n    test_widget = play_audio(test_audio_path)\n    if test_widget:\n        ipd.display(test_widget)\n    \n    # 3. Visualize test audio\n    print(\"\\n📊 VISUALIZING TEST AUDIO:\")\n    visualize_audio(test_audio_path)\n    \n    # 4. Compare with predicted speaker samples\n    print(\"\\n\" + \"=\"*80)\n    print(\"STEP 3: COMPARE WITH PREDICTED SPEAKER SAMPLES\")\n    print(\"=\"*80 + \"\\n\")\n    \n    compare_audio_samples(\n        test_audio_path, \n        predicted_speaker, \n        data_path, \n        num_comparisons\n    )\n    \n    # 5. Spectrogram comparison (if available)\n    if show_spectrograms:\n        comparison_files = find_speaker_samples(predicted_speaker, data_path, max_samples=1)\n        if comparison_files:\n            print(\"\\n\" + \"=\"*80)\n            print(\"STEP 4: SPECTROGRAM COMPARISON\")\n            print(\"=\"*80 + \"\\n\")\n            \n            compare_spectrograms(\n                test_audio_path,\n                comparison_files[0],\n                label1=\"Your Test Audio\",\n                label2=f\"Speaker {predicted_speaker} Sample\"\n            )\n    \n    print(\"\\n\" + \"=\"*80)\n    print(\"✓ ANALYSIS COMPLETE!\")\n    print(\"=\"*80 + \"\\n\")\n    \n    print(\"💡 Next Steps:\")\n    print(\"   • Listen to both the test audio and comparison samples\")\n    print(\"   • Do they sound like the same person?\")\n    print(\"   • Check the spectrograms - similar patterns = likely same speaker\")\n    print(\"   • If prediction seems wrong, the speaker might not be in training set\")\n    print()\n    \n    return predicted_speaker, confidence, top_speakers, top_confidences\n\n\n# ==================== USAGE EXAMPLES ====================\n\nif __name__ == \"__main__\":\n    print(\"\\n\" + \"=\"*80)\n    print(\" \"*20 + \"AUDIO PLAYBACK TOOL - USAGE GUIDE\")\n    print(\"=\"*80 + \"\\n\")\n    \n    print(\"📖 AVAILABLE FUNCTIONS:\\n\")\n    \n    print(\"1️⃣  PLAY AUDIO:\")\n    print(\"    >>> audio = play_audio('/path/to/audio.wav')\")\n    print(\"    >>> ipd.display(audio)\")\n    print()\n    \n    print(\"2️⃣  VISUALIZE AUDIO:\")\n    print(\"    >>> visualize_audio('/path/to/audio.wav')\")\n    print()\n    \n    print(\"3️⃣  FIND SPEAKER SAMPLES:\")\n    print(\"    >>> samples = find_speaker_samples('ec201020', num_samples=5)\")\n    print()\n    \n    print(\"4️⃣  COMPARE AUDIO:\")\n    print(\"    >>> compare_audio_samples(\")\n    print(\"            test_audio='/path/to/test.wav',\")\n    print(\"            predicted_speaker_id='ec201020',\")\n    print(\"            num_comparisons=3\")\n    print(\"        )\")\n    print()\n    \n    print(\"5️⃣  COMPARE SPECTROGRAMS:\")\n    print(\"    >>> compare_spectrograms(\")\n    print(\"            audio1_path='/path/to/test.wav',\")\n    print(\"            audio2_path='/path/to/reference.wav'\")\n    print(\"        )\")\n    print()\n    \n    print(\"6️⃣  ALL-IN-ONE (RECOMMENDED):\")\n    print(\"    >>> predict_and_listen(\")\n    print(\"            test_audio_path='/path/to/test.wav',\")\n    print(\"            model=model,\")\n    print(\"            label_encoder=label_encoder,\")\n    print(\"            num_comparisons=3\")\n    print(\"        )\")\n    print()\n    \n    print(\"=\"*80 + \"\\n\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T16:16:59.836702Z","iopub.execute_input":"2025-11-11T16:16:59.837066Z","iopub.status.idle":"2025-11-11T16:16:59.953856Z","shell.execute_reply.started":"2025-11-11T16:16:59.837009Z","shell.execute_reply":"2025-11-11T16:16:59.953006Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"ls '/kaggle/working/train/audio/dog' | head -n 10","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T16:19:14.745949Z","iopub.execute_input":"2025-11-11T16:19:14.746290Z","iopub.status.idle":"2025-11-11T16:19:15.940552Z","shell.execute_reply.started":"2025-11-11T16:19:14.746246Z","shell.execute_reply":"2025-11-11T16:19:15.939656Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"results = predict_speaker(model, '/kaggle/working/train/audio/dog/00f0204f_nohash_0.wav', label_encoder)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-11T16:19:41.358717Z","iopub.execute_input":"2025-11-11T16:19:41.359031Z","iopub.status.idle":"2025-11-11T16:19:41.390716Z","shell.execute_reply.started":"2025-11-11T16:19:41.358986Z","shell.execute_reply":"2025-11-11T16:19:41.389654Z"}},"outputs":[],"execution_count":null}]}