{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3"},"language_info":{"name":"python"},"colab":{"provenance":[]},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":119642,"databundleVersionId":14303609,"sourceType":"competition"}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport os","metadata":{"id":"JeBcKI3clwdX"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df = pd.read_csv('/kaggle/input/fake-news-detection-projectx/train.csv')","metadata":{"id":"M4ZiyTgyq_c9"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df.head()","metadata":{"id":"gA933KQMrSz-","outputId":"2bf185d5-780b-43f2-9de5-d1ed2af560bf"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df.info()","metadata":{"id":"INl7YhKBrca7","outputId":"66a18469-0d51-40b7-cae8-dbcb84ec2058"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df.shape","metadata":{"id":"-VCLLhZ_sAa-","outputId":"816fa56d-1c53-44ae-a4b3-2c9919ff959d"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df.isnull().sum()","metadata":{"id":"xSEx5nztrxu6","outputId":"8e8555f6-6863-4250-cc7c-795dbc43d5d5"},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Filling empty text with Title","metadata":{"id":"k05VITHnwAFA"}},{"cell_type":"code","source":"train_df['text'] = train_df['text'].fillna(train_df['title'])","metadata":{"id":"Qe6BFpeLr71B"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df.isnull().sum()","metadata":{"id":"-rjfppTXv8iC","outputId":"bd19ffa1-fd97-4949-ce46-e06400e1991c"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df.head()","metadata":{"id":"SUBRiQM9v91H","outputId":"5fa8b5af-2086-4f35-cdd3-4aea4d04ab9d"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df","metadata":{"id":"dRZFMsiOwuNw","outputId":"dbb4d237-6d15-4a2f-f838-f5a853674bbd"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X = train_df.drop('type', axis=1)\ny = train_df['type']","metadata":{"id":"FWYF0kKCxU8e"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X.shape","metadata":{"id":"uldJdGaSxa9b","outputId":"636e2067-33e6-42e3-a33e-cc602e9c41e1"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"y.shape","metadata":{"id":"XpzFfiIHxg8v","outputId":"08a73aad-1e6c-40d2-9d75-cd4f5599be4b"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf","metadata":{"id":"mYmDWC0wxiQ8"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"tf.__version__","metadata":{"id":"64cmweF0xm31","outputId":"250c5bb9-a6c3-4482-cac0-b3f6d097dac7"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.layers import Embedding\nfrom tensorflow.keras.preprocessing.sequence import pad_sequences\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.preprocessing.text import one_hot\nfrom tensorflow.keras.layers import LSTM\nfrom tensorflow.keras.layers import Dense","metadata":{"id":"Mwqk1LSbxpkZ"},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## One-Hot Representation","metadata":{"id":"5YOU--8myfA2"}},{"cell_type":"code","source":"messages = X.copy()","metadata":{"id":"ox9jAxrwxvU5"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"messages.reset_index(inplace=True)","metadata":{"id":"G6oa6sl7yVl_"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"messages","metadata":{"id":"TItM319UynO2","outputId":"0d985cb6-50af-4951-8d4a-a478ebb87cbb"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import nltk\nimport re\nfrom nltk.corpus import stopwords","metadata":{"id":"26MQyqDkyoSi"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"nltk.download('stopwords')","metadata":{"id":"88801WWMzIIH","outputId":"a2ff095f-7c16-4086-98b3-8f80143df81f"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from nltk.stem.porter import PorterStemmer\nps = PorterStemmer()\ncorpus = []\nfor i in range(0, len(messages)):\n  review = re.sub('[^a-zA-Z]', ' ', messages['title'][i])\n  review = review.lower()\n  review = review.split()\n\n  review = [ps.stem(word) for word in review if not word in stopwords.words('english')]\n  review = ' '.join(review)\n  corpus.append(review)","metadata":{"id":"5txuLR5azJxs"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"corpus","metadata":{"id":"z9we6UPaztYh","outputId":"74e24d46-d2bc-4ea0-dd28-a046d6712903"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"corpus[2]","metadata":{"id":"SiOKKhpSz9ZQ","outputId":"07e25dda-20e7-42d7-dfbd-7e1707ae3b9a"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"voc_size = 5000","metadata":{"id":"bLXZeyx3XSzV"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"onehot_repr = [one_hot(words, voc_size) for words in corpus]\nonehot_repr","metadata":{"id":"Uk75USpfXdcN","outputId":"7a3642a1-016b-46d8-dc83-2625f84d257e"},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Embedding Representation","metadata":{"id":"w2ABiBjvXrIX"}},{"cell_type":"code","source":"onehot_repr[2]","metadata":{"id":"TkuhFSXkXigc","outputId":"5e766096-7a6b-463d-af1d-3f52eea2adf1"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sent_length = 20\nembedded_docs = pad_sequences(onehot_repr, padding='pre', maxlen=sent_length)\nprint(embedded_docs)","metadata":{"id":"w4Lcq3MzX3lk","outputId":"058b202d-64e6-4424-f9ea-6872d6af0d68"},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Creating LSTM Model and Adding Embedding Layer","metadata":{"id":"3TKJ9OXVYrVw"}},{"cell_type":"code","source":"from tensorflow.keras.layers import Embedding, LSTM, Dense, Input\n\nembedding_vector_features = 100\nmodel = Sequential()\nmodel.add(Input(shape=(sent_length, )))\nmodel.add(Embedding(voc_size, embedding_vector_features))\nmodel.add(LSTM(100, dropout = 0.2))\nmodel.add(Dense(1, activation='sigmoid'))\nmodel.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])\nprint(model.summary())","metadata":{"id":"lOplCBdgYPSZ","outputId":"073f41b3-00d3-4138-9ed2-66cdd738b935"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X_final = np.array(embedded_docs)\ny_final = np.array(y)","metadata":{"id":"Z4dcNy_zajui"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X_final.shape, y_final.shape","metadata":{"id":"Yhioay2Ybu9d","outputId":"77142460-2fd5-4407-c020-62f72eed19aa"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nX_train, X_val, y_train, y_val = train_test_split(X_final, y_final, test_size=0.2, random_state=42)","metadata":{"id":"p8wOyEnRb0aF"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"history = model.fit(X_train, y_train, validation_data=(X_val, y_val), epochs=10, batch_size=64)","metadata":{"id":"aa-D9cLTcKqC","outputId":"980d8bcc-31b7-4445-9cf5-df7b01b4a302"},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Visualization of Training Set","metadata":{"id":"ZTpmIEbidSTh"}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\n# Plot accuracy\nplt.figure(figsize=(12, 4))\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('Model Accuracy')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.legend()\nplt.grid(True)\n\n# Plot loss\nplt.subplot(1, 2, 2)\nplt.plot(history.history['loss'], label='Training Loss')\nplt.plot(history.history['val_loss'], label='Validation Loss')\nplt.title('Model Loss')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.legend()\nplt.grid(True)\n\nplt.tight_layout()\nplt.show()","metadata":{"id":"iamp7j-XcMfB","outputId":"27fae904-0820-4679-f705-f45a744b1602"},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Evaluation of Training Set","metadata":{"id":"lK-wwmX8eOVl"}},{"cell_type":"code","source":"from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, classification_report\n\n# Predictions on training data\ny_train_pred_prob = model.predict(X_train)\ny_train_pred = (y_train_pred_prob > 0.5).astype(int)\n\n# Calculate metrics\ntrain_accuracy = accuracy_score(y_train, y_train_pred)\ntrain_precision = precision_score(y_train, y_train_pred)\ntrain_recall = recall_score(y_train, y_train_pred)\ntrain_f1 = f1_score(y_train, y_train_pred)\n\nprint(\"=\" * 50)\nprint(\"TRAINING SET METRICS\")\nprint(\"=\" * 50)\nprint(f\"Accuracy:  {train_accuracy:.4f}\")\nprint(f\"Precision: {train_precision:.4f}\")\nprint(f\"Recall:    {train_recall:.4f}\")\nprint(f\"F1-Score:  {train_f1:.4f}\")\nprint(\"\\n\" + classification_report(y_train, y_train_pred, target_names=['Real', 'Fake']))","metadata":{"id":"zJRo69a8dy8s","outputId":"99f7ff25-11fc-4b71-cbfe-27ca69324b7a"},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Evaluation and Visualization of Validaiton Set","metadata":{"id":"TzeBDQzFeUA7"}},{"cell_type":"code","source":"from sklearn.metrics import classification_report, confusion_matrix, accuracy_score\n\n# Get predictions on validation set\ny_val_pred_prob = model.predict(X_val)\ny_val_pred = (y_val_pred_prob > 0.5).astype(int)\n\n# Calculate metrics\nprint(\"Validation Accuracy:\", accuracy_score(y_val, y_val_pred))\nprint(\"\\nClassification Report:\")\nprint(classification_report(y_val, y_val_pred, target_names=['Real', 'Fake']))\n\n# Confusion Matrix\nprint(\"\\nConfusion Matrix:\")\ncm = confusion_matrix(y_val, y_val_pred)\nprint(cm)\n\n# Visualize confusion matrix\nimport seaborn as sns\nplt.figure(figsize=(6, 5))\nsns.heatmap(cm, annot=True, fmt='d', cmap='Blues',\n            xticklabels=['Real', 'Fake'],\n            yticklabels=['Real', 'Fake'])\nplt.title('Confusion Matrix')\nplt.ylabel('True Label')\nplt.xlabel('Predicted Label')\nplt.show()","metadata":{"id":"qpLrj1X9d_PZ","outputId":"a765488a-8038-47ae-ac8a-f7c4d108135e"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Predictions on validation data\ny_val_pred_prob = model.predict(X_val)\ny_val_pred = (y_val_pred_prob > 0.5).astype(int)\n\n# Calculate metrics\nval_accuracy = accuracy_score(y_val, y_val_pred)\nval_precision = precision_score(y_val, y_val_pred)\nval_recall = recall_score(y_val, y_val_pred)\nval_f1 = f1_score(y_val, y_val_pred)\n\nprint(\"=\" * 50)\nprint(\"VALIDATION SET METRICS\")\nprint(\"=\" * 50)\nprint(f\"Accuracy:  {val_accuracy:.4f}\")\nprint(f\"Precision: {val_precision:.4f}\")\nprint(f\"Recall:    {val_recall:.4f}\")\nprint(f\"F1-Score:  {val_f1:.4f}\")\nprint(\"\\n\" + classification_report(y_val, y_val_pred, target_names=['Real', 'Fake']))","metadata":{"id":"wS8Df_f7eJFd","outputId":"755444e2-3051-4f19-e1ee-e5fc02f05ac3"},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Running the model on Test Data","metadata":{"id":"IivkTKrTfwL1"}},{"cell_type":"code","source":"test_df = pd.read_csv('/kaggle/input/fake-news-detection-projectx/test.csv')","metadata":{"id":"UrXZF1Gkev9K"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df.head()","metadata":{"id":"bMkqeiw0f00u","outputId":"d8cd2a23-b249-45de-a8ce-471bbfb4f6e8"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df","metadata":{"id":"jfpIhDHof3bw","outputId":"ac9eb67e-28df-447d-a9fe-ad10335b0fdf"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df.shape","metadata":{"id":"fkC9rAO0f8WF","outputId":"e2f69261-0cde-4baa-c2f6-db3c41ef4b87"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df.info()","metadata":{"id":"GjzZNSc1gDdi","outputId":"9e625416-479b-47dc-d006-d131b6956683"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_df.isna().sum()","metadata":{"id":"TMRZZkiTgFih","outputId":"3ba501ad-7e5b-4070-d49b-130fa454668a"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\n\" + \"=\" * 60)\nprint(\"STEP 2: PREPROCESSING TEST DATA (SAME AS TRAINING)\")\nprint(\"=\" * 60)\n\nimport nltk\nimport re\nfrom nltk.corpus import stopwords\nfrom nltk.stem.porter import PorterStemmer\n\n# Initialize stemmer (same as training)\nps = PorterStemmer()\n\n# Create corpus from test data titles (same process as training)\ntest_corpus = []\n\nprint(\"\\nProcessing test titles...\")\nfor i in range(0, len(test_df)):\n    # Remove non-alphabetic characters\n    review = re.sub('[^a-zA-Z]', ' ', test_df['title'].iloc[i])\n    # Convert to lowercase\n    review = review.lower()\n    # Split into words\n    review = review.split()\n    # Stem and remove stopwords\n    review = [ps.stem(word) for word in review if not word in stopwords.words('english')]\n    # Join back to string\n    review = ' '.join(review)\n    test_corpus.append(review)\n\nprint(f\"✓ Processed {len(test_corpus)} test titles\")\n\n# Show samples of processed text\nprint(\"\\n\" + \"=\" * 60)\nprint(\"SAMPLE PROCESSED TITLES\")\nprint(\"=\" * 60)\nprint(\"\\nOriginal vs Processed:\")\nfor i in range(3):\n    print(f\"\\n{i+1}. Original: {test_df['title'].iloc[i][:70]}...\")\n    print(f\"   Processed: {test_corpus[i][:70]}...\")\n\nprint(\"\\n\" + \"=\" * 60)\nprint(\"PREPROCESSING COMPLETE\")\nprint(\"=\" * 60)","metadata":{"id":"OkEyuTLSgJar","outputId":"22ef2ab8-46d6-4384-cf81-74397ba4aee5"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tensorflow.keras.preprocessing.text import one_hot\n\nprint(\"\\n\" + \"=\" * 60)\nprint(\"STEP 3: ONE-HOT ENCODING TEST DATA\")\nprint(\"=\" * 60)\n\n# Use the same vocabulary size as training\nvoc_size = 5000\n\n# Apply one-hot encoding to test corpus\ntest_onehot_repr = [one_hot(words, voc_size) for words in test_corpus]\n\nprint(f\"✓ One-hot encoding completed\")\nprint(f\"  Vocabulary size: {voc_size}\")\nprint(f\"  Encoded samples: {len(test_onehot_repr)}\")\n\n# Show sample encoded sequences\nprint(\"\\n\" + \"=\" * 60)\nprint(\"SAMPLE ENCODED SEQUENCES\")\nprint(\"=\" * 60)\nfor i in range(3):\n    print(f\"\\n{i+1}. Processed text: {test_corpus[i][:50]}...\")\n    print(f\"   Encoded sequence: {test_onehot_repr[i][:15]}...\")  # Show first 15 numbers\n    print(f\"   Sequence length: {len(test_onehot_repr[i])} words\")\n\nprint(\"\\n\" + \"=\" * 60)\nprint(\"ONE-HOT ENCODING COMPLETE\")\nprint(\"=\" * 60)","metadata":{"id":"Pqi9Ahaihi67","outputId":"7fab4fe2-eafd-4265-caf3-64c19627957b"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\n\" + \"=\" * 60)\nprint(\"STEP 4: PADDING SEQUENCES\")\nprint(\"=\" * 60)\n\n# Use the same sentence length as training\nsent_length = 20\n\n# Pad sequences (same parameters as training)\ntest_embedded_docs = pad_sequences(test_onehot_repr, padding='pre', maxlen=sent_length)\n\nprint(f\"✓ Padding completed\")\nprint(f\"  Padding type: 'pre' (zeros added at the beginning)\")\nprint(f\"  Max length: {sent_length}\")\nprint(f\"  Shape: {test_embedded_docs.shape}\")\n\n# Show sample padded sequences\nprint(\"\\n\" + \"=\" * 60)\nprint(\"SAMPLE PADDED SEQUENCES\")\nprint(\"=\" * 60)\nfor i in range(3):\n    print(f\"\\n{i+1}. Original length: {len(test_onehot_repr[i])} words\")\n    print(f\"   Padded length: {len(test_embedded_docs[i])} words\")\n    print(f\"   Padded sequence: {test_embedded_docs[i]}\")\n    print(f\"   (Note: Zeros added at the beginning if length < {sent_length})\")\n\n# Convert to numpy array (final format for model)\nX_test_final = np.array(test_embedded_docs)\n\nprint(\"\\n\" + \"=\" * 60)\nprint(\"FINAL TEST DATA PREPARED\")\nprint(\"=\" * 60)\nprint(f\"✓ X_test_final shape: {X_test_final.shape}\")\nprint(f\"  - {X_test_final.shape[0]} samples\")\nprint(f\"  - {X_test_final.shape[1]} features (padded length)\")\nprint(f\"\\n✓ Test data is ready for prediction!\")\n","metadata":{"id":"faM1GpbriqL1","outputId":"a5b18c23-4c87-46ea-d06f-6e28f7152c1a"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\n\" + \"=\" * 60)\nprint(\"STEP 5: MAKING PREDICTIONS ON TEST DATA\")\nprint(\"=\" * 60)\n\n# Make predictions (get probabilities)\nprint(\"\\nGenerating predictions...\")\ny_test_pred_prob = model.predict(X_test_final, verbose=1)\n\nprint(f\"\\n✓ Predictions completed!\")\nprint(f\"  Prediction shape: {y_test_pred_prob.shape}\")\nprint(f\"  Total predictions: {len(y_test_pred_prob)}\")\n\n# Convert probabilities to binary predictions (0 or 1)\n# Threshold: 0.5 (if probability > 0.5, predict Fake=1, else Real=0)\ny_test_pred = (y_test_pred_prob > 0.5).astype(int).flatten()\n\nprint(\"\\n\" + \"=\" * 60)\nprint(\"PREDICTION SUMMARY\")\nprint(\"=\" * 60)\nprint(f\"Total predictions: {len(y_test_pred)}\")\nprint(f\"Predicted as Real (0): {(y_test_pred == 0).sum()} ({(y_test_pred == 0).sum()/len(y_test_pred)*100:.1f}%)\")\nprint(f\"Predicted as Fake (1): {(y_test_pred == 1).sum()} ({(y_test_pred == 1).sum()/len(y_test_pred)*100:.1f}%)\")\n\n# Show sample predictions\nprint(\"\\n\" + \"=\" * 60)\nprint(\"SAMPLE PREDICTIONS (First 10)\")\nprint(\"=\" * 60)\nprint(f\"{'ID':<8} {'Title':<50} {'Probability':<12} {'Prediction':<12}\")\nprint(\"-\" * 82)\nfor i in range(10):\n    title = test_df['title'].iloc[i][:47] + \"...\" if len(test_df['title'].iloc[i]) > 50 else test_df['title'].iloc[i]\n    prob = y_test_pred_prob[i][0]\n    pred = \"Fake\" if y_test_pred[i] == 1 else \"Real\"\n    print(f\"{test_df['id'].iloc[i]:<8} {title:<50} {prob:<12.4f} {pred:<12}\")\n\nprint(\"\\n\" + \"=\" * 60)\nprint(\"PREDICTIONS COMPLETE\")\nprint(\"=\" * 60)","metadata":{"id":"EvEzi3wDi0VF","outputId":"0c266b8f-8cf3-4a53-83ca-e62373872c9f"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"\\n\" + \"=\" * 60)\nprint(\"STEP 6: CREATING SUBMISSION FILE\")\nprint(\"=\" * 60)\n\n# Create submission dataframe (id and prediction only)\nsubmission = pd.DataFrame({\n    'id': test_df['id'],\n    'prediction': y_test_pred\n})\n\n# Show first few rows\nprint(\"\\nSubmission file preview (first 15 rows):\")\nprint(submission.head(15))\n\nprint(f\"\\n✓ Submission shape: {submission.shape}\")\nprint(f\"  - {submission.shape[0]} predictions\")\nprint(f\"  - {submission.shape[1]} columns (id, prediction)\")\n\n# Check distribution\nprint(\"\\n\" + \"=\" * 60)\nprint(\"PREDICTION DISTRIBUTION\")\nprint(\"=\" * 60)\nprint(submission['prediction'].value_counts().sort_index())\nprint(f\"\\nReal (0): {(submission['prediction'] == 0).sum()} ({(submission['prediction'] == 0).sum()/len(submission)*100:.1f}%)\")\nprint(f\"Fake (1): {(submission['prediction'] == 1).sum()} ({(submission['prediction'] == 1).sum()/len(submission)*100:.1f}%)\")\n\n# Validation checks\nprint(\"\\n\" + \"=\" * 60)\nprint(\"VALIDATION CHECKS\")\nprint(\"=\" * 60)\nprint(f\"✓ All IDs present: {len(submission) == len(test_df)}\")\nprint(f\"✓ No missing values: {submission.isnull().sum().sum() == 0}\")\nprint(f\"✓ Predictions are binary (0 or 1): {set(submission['prediction'].unique()) == {0, 1}}\")\nprint(f\"✓ No duplicate IDs: {submission['id'].nunique() == len(submission)}\")\n\n# Save submission file\nsubmission.to_csv('submission.csv', index=False)\n\nprint(\"\\n\" + \"=\" * 60)\nprint(\"✅ SUBMISSION FILE CREATED SUCCESSFULLY!\")\nprint(\"=\" * 60)\nprint(\"File name: 'submission.csv'\")\nprint(\"Format: CSV with 'id' and 'prediction' columns\")\nprint(\"Total rows:\", len(submission))","metadata":{"id":"I1LSyXjLi8-c","outputId":"ec1ee7cb-6338-4385-bc0c-ed842caa637c"},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"id":"fg0BeSUqjZE-"},"outputs":[],"execution_count":null}]}