{"cells":[{"cell_type":"markdown","metadata":{},"source":"# Akkadian Translation - Ensemble Inference (5-Fold CV)\nThis notebook uses ensemble predictions from all 5 cross-validation folds for better accuracy."},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"import os\nimport gc\nimport pandas as pd\nimport torch\nimport numpy as np\nfrom transformers import AutoTokenizer, AutoModelForSeq2SeqLM\nfrom torch.utils.data import DataLoader, Dataset\nfrom tqdm import tqdm\n\n# Configuration\nMODEL_NAME = \"google/byt5-small\"\nCV_MODELS_PATH = \"/kaggle/input/byt5-akkadian-english-5fold-ensemble/byt5-akkadian-cv\"  # Kaggle dataset path\nSINGLE_MODEL_PATH = \"./byt5-akkadian-model\"  # Fallback to single model\nMAX_LENGTH = 512\nBATCH_SIZE = 8\nN_FOLDS = 5\nINPUT_DIR = \"/kaggle/input/deep-past-initiative-machine-translation\"\n\nprint(f\"Using device: {('cuda' if torch.cuda.is_available() else 'cpu')}\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# Load data\ntest_df = pd.read_csv(f\"{INPUT_DIR}/test.csv\")\nsample_sub = pd.read_csv(f\"{INPUT_DIR}/sample_submission.csv\")\n\nprint(f\"Test samples: {len(test_df)}\")\nprint(f\"Submission format: {sample_sub.shape}\")\ntest_df.head()"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# Check which models are available\ncv_models_exist = all(os.path.exists(f\"{CV_MODELS_PATH}/fold_{i}/checkpoint-final\") \n                     for i in range(N_FOLDS))\nsingle_model_exists = os.path.exists(f\"{SINGLE_MODEL_PATH}\")\n\nprint(f\"CV models available: {cv_models_exist}\")\nprint(f\"Single model available: {single_model_exists}\")\n\nUSE_CV = cv_models_exist\n\nif not USE_CV and not single_model_exists:\n    raise ValueError(\"No trained models found! Please train a model first.\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# Create dataset\nPREFIX = \"translate Akkadian to English: \"\n\nclass AkkadianDataset(Dataset):\n    def __init__(self, df, tokenizer, max_length=512):\n        self.df = df\n        self.tokenizer = tokenizer\n        self.max_length = max_length\n    \n    def __len__(self):\n        return len(self.df)\n    \n    def __getitem__(self, idx):\n        source_text = str(self.df.iloc[idx]['transliteration'])\n        inputs = self.tokenizer(\n            PREFIX + source_text,\n            max_length=self.max_length,\n            truncation=True,\n            padding=\"max_length\",\n            return_tensors=\"pt\"\n        )\n        return {\n            \"input_ids\": inputs[\"input_ids\"].squeeze(),\n            \"attention_mask\": inputs[\"attention_mask\"].squeeze()\n        }\n\n# Load tokenizer\ntokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)\ntest_dataset = AkkadianDataset(test_df, tokenizer, MAX_LENGTH)\ntest_loader = DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=False)\nprint(f\"Created dataloader with {len(test_loader)} batches\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# Function to generate predictions from a single model\ndef generate_predictions(model_path, fold_idx=None):\n    print(f\"\\nLoading model from {model_path}...\")\n    \n    model = AutoModelForSeq2SeqLM.from_pretrained(\n        model_path,\n        low_cpu_mem_usage=True,\n        torch_dtype=torch.float16  # Use half precision to save memory\n    )\n    device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n    model.to(device)\n    model.eval()\n    \n    all_predictions = []\n    \n    with torch.no_grad():\n        for batch in tqdm(test_loader, desc=f\"Fold {fold_idx}\" if fold_idx else \"Generating\"):\n            input_ids = batch[\"input_ids\"].to(device)\n            attention_mask = batch[\"attention_mask\"].to(device)\n            \n            generated = model.generate(\n                input_ids=input_ids,\n                attention_mask=attention_mask,\n                max_length=MAX_LENGTH,\n                num_beams=4,\n                early_stopping=True\n            )\n            \n            batch_predictions = tokenizer.batch_decode(generated, skip_special_tokens=True)\n            all_predictions.extend([pred.strip() for pred in batch_predictions])\n    \n    # Cleanup\n    del model\n    gc.collect()\n    torch.cuda.empty_cache()\n    \n    return all_predictions"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# Generate predictions using ensemble\nif USE_CV:\n    print(\"Using 5-fold ensemble for predictions...\")\n    \n    # Store predictions from each fold\n    fold_predictions = []\n    \n    for fold_idx in range(N_FOLDS):\n        fold_path = f\"{CV_MODELS_PATH}/fold_{fold_idx}/checkpoint-final\"\n        \n        # Check for alternative checkpoint name\n        if not os.path.exists(fold_path):\n            # Try to find any checkpoint folder\n            import glob\n            checkpoints = glob.glob(f\"{CV_MODELS_PATH}/fold_{fold_idx}/checkpoint-*\")\n            if checkpoints:\n                fold_path = checkpoints[0]\n            else:\n                # Use the folder directly if no checkpoint subfolder\n                fold_path = f\"{CV_MODELS_PATH}/fold_{fold_idx}\"\n        \n        preds = generate_predictions(fold_path, fold_idx)\n        fold_predictions.append(preds)\n    \n    # Ensemble by voting (most common prediction)\n    print(\"\\nEnsembling predictions...\")\n    final_predictions = []\n    \n    for i in range(len(test_df)):\n        candidates = [fold_predictions[f][i] for f in range(N_FOLDS)]\n        # Use most common, or first if tie\n        from collections import Counter\n        most_common = Counter(candidates).most_common(1)[0][0]\n        final_predictions.append(most_common)\n    \nelse:\n    print(\"Using single model for predictions...\")\n    final_predictions = generate_predictions(SINGLE_MODEL_PATH)\n\nprint(f\"\\nGenerated {len(final_predictions)} translations!\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# Show sample predictions\nfor i in range(min(5, len(final_predictions))):\n    print(f\"\\n--- Sample {i+1} ---\")\n    print(f\"Input: {test_df.iloc[i]['transliteration']}\")\n    print(f\"Translation: {final_predictions[i]}\")"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# Create submission file\nsubmission = pd.DataFrame({\n    'id': test_df['id'],\n    'translation': final_predictions\n})\n\n# Save submission\nsubmission.to_csv('submission.csv', index=False)\nprint(\"\\n✓ Submission saved to submission.csv\")\nprint(f\"Total rows: {len(submission)}\")\n\n# Display first few rows\nsubmission.head()"},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":"# Validate submission format\nprint(\"\\n=== Validation ===\")\nprint(f\"Expected columns: {list(sample_sub.columns)}\")\nprint(f\"Your columns: {list(submission.columns)}\")\nprint(f\"Shape matches: {submission.shape == sample_sub.shape}\")\nprint(f\"No null values: {submission.isnull().sum().sum() == 0}\")\nprint(f\"All IDs present: {set(submission['id']) == set(sample_sub['id'])}\")\nprint(\"\\n✓ Ready to submit!\")"}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.8.0"}},"nbformat":4,"nbformat_minor":4}