{"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":"gpu","dataSources":[{"sourceId":121150,"databundleVersionId":15061024,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":14283854,"sourceType":"datasetVersion","datasetId":9116983}],"dockerImageVersionId":31234,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-12-24T17:42:05.972753Z","iopub.execute_input":"2025-12-24T17:42:05.973813Z","iopub.status.idle":"2025-12-24T17:42:05.982534Z","shell.execute_reply.started":"2025-12-24T17:42:05.973784Z","shell.execute_reply":"2025-12-24T17:42:05.981935Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Please upvote👍 if this seems to be helpful!","metadata":{}},{"cell_type":"code","source":"import os\nimport pandas as pd\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom transformers import (\n    MarianMTModel, \n    MarianTokenizer,\n    get_linear_schedule_with_warmup\n)\nfrom torch.optim import AdamW\nfrom sklearn.model_selection import train_test_split\nfrom tqdm import tqdm\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# ============================================================================\n# Configuration\n# ============================================================================\nclass Config:\n    \"\"\"Centralized configuration\"\"\"\n    DATA_PATH = '/kaggle/input/deep-past-initiative-machine-translation/'\n    \n    # Model configuration\n    MODEL_NAME = 'Helsinki-NLP/opus-mt-mul-en'\n    \n    # FOR STEP 1 (Download model): Set this path to save the model\n    MODEL_SAVE_PATH = './saved_model'\n    \n    # FOR STEP 2 (Load from dataset): Set this to your Kaggle dataset path\n    # After uploading the model as a dataset, update this path\n    MODEL_DATASET_PATH = '/kaggle/input/marian-mt-model/saved_model'  \n    \n    # Set this to True for Step 1 (download), False for Step 2 (load from dataset)\n    DOWNLOAD_MODE = False\n    \n    MAX_LENGTH = 128\n    \n    # Training configuration\n    BATCH_SIZE = 16\n    EPOCHS = 5\n    LEARNING_RATE = 3e-5\n    WARMUP_STEPS = 500\n    VAL_SPLIT = 0.1\n    RANDOM_STATE = 42\n    \n    # Device\n    DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n\n# ============================================================================\n# Dataset Class\n# ============================================================================\nclass TranslationDataset(Dataset):\n    \"\"\"Dataset for Akkadian-English translation\"\"\"\n    \n    def __init__(self, src_texts, tgt_texts, tokenizer, max_length):\n        self.src_texts = src_texts\n        self.tgt_texts = tgt_texts\n        self.tokenizer = tokenizer\n        self.max_length = max_length\n    \n    def __len__(self):\n        return len(self.src_texts)\n    \n    def __getitem__(self, idx):\n        src_text = str(self.src_texts[idx])\n        tgt_text = str(self.tgt_texts[idx]) if self.tgt_texts is not None else \"\"\n        \n        # Tokenize source text\n        src_encoding = self.tokenizer(\n            src_text,\n            max_length=self.max_length,\n            padding='max_length',\n            truncation=True,\n            return_tensors='pt'\n        )\n        \n        # Tokenize target text\n        if tgt_text:\n            tgt_encoding = self.tokenizer(\n                tgt_text,\n                max_length=self.max_length,\n                padding='max_length',\n                truncation=True,\n                return_tensors='pt'\n            )\n            labels = tgt_encoding['input_ids'].squeeze()\n            labels[labels == self.tokenizer.pad_token_id] = -100\n        else:\n            labels = torch.zeros(self.max_length, dtype=torch.long)\n        \n        return {\n            'input_ids': src_encoding['input_ids'].squeeze(),\n            'attention_mask': src_encoding['attention_mask'].squeeze(),\n            'labels': labels\n        }\n\n# ============================================================================\n# Training Class\n# ============================================================================\nclass Trainer:\n    \"\"\"Handles model training and evaluation\"\"\"\n    \n    def __init__(self, model, tokenizer, config):\n        self.model = model\n        self.tokenizer = tokenizer\n        self.config = config\n        self.device = config.DEVICE\n        self.model.to(self.device)\n    \n    def train_epoch(self, train_loader, optimizer, scheduler):\n        \"\"\"Train for one epoch\"\"\"\n        self.model.train()\n        total_loss = 0\n        \n        progress_bar = tqdm(train_loader, desc='Training')\n        for batch in progress_bar:\n            input_ids = batch['input_ids'].to(self.device)\n            attention_mask = batch['attention_mask'].to(self.device)\n            labels = batch['labels'].to(self.device)\n            \n            outputs = self.model(\n                input_ids=input_ids,\n                attention_mask=attention_mask,\n                labels=labels\n            )\n            \n            loss = outputs.loss\n            total_loss += loss.item()\n            \n            optimizer.zero_grad()\n            loss.backward()\n            torch.nn.utils.clip_grad_norm_(self.model.parameters(), 1.0)\n            optimizer.step()\n            scheduler.step()\n            \n            progress_bar.set_postfix({'loss': loss.item()})\n        \n        return total_loss / len(train_loader)\n    \n    def evaluate(self, val_loader):\n        \"\"\"Evaluate on validation set\"\"\"\n        self.model.eval()\n        total_loss = 0\n        \n        with torch.no_grad():\n            for batch in tqdm(val_loader, desc='Evaluating'):\n                input_ids = batch['input_ids'].to(self.device)\n                attention_mask = batch['attention_mask'].to(self.device)\n                labels = batch['labels'].to(self.device)\n                \n                outputs = self.model(\n                    input_ids=input_ids,\n                    attention_mask=attention_mask,\n                    labels=labels\n                )\n                \n                total_loss += outputs.loss.item()\n        \n        return total_loss / len(val_loader)\n    \n    def predict(self, test_loader):\n        \"\"\"Generate predictions\"\"\"\n        self.model.eval()\n        predictions = []\n        \n        with torch.no_grad():\n            for batch in tqdm(test_loader, desc='Predicting'):\n                input_ids = batch['input_ids'].to(self.device)\n                attention_mask = batch['attention_mask'].to(self.device)\n                \n                outputs = self.model.generate(\n                    input_ids=input_ids,\n                    attention_mask=attention_mask,\n                    max_length=self.config.MAX_LENGTH,\n                    num_beams=4,\n                    early_stopping=True,\n                    no_repeat_ngram_size=2\n                )\n                \n                batch_predictions = self.tokenizer.batch_decode(\n                    outputs, \n                    skip_special_tokens=True\n                )\n                predictions.extend(batch_predictions)\n        \n        return predictions\n\n# ============================================================================\n# Model Download and Save (STEP 1)\n# ============================================================================\ndef download_and_save_model(config):\n    \"\"\"Download model from Hugging Face and save locally\"\"\"\n    print(\"=\"*80)\n    print(\"STEP 1: DOWNLOADING MODEL FROM HUGGING FACE\")\n    print(\"=\"*80)\n    print(f\"\\n📥 Downloading {config.MODEL_NAME}...\")\n    \n    tokenizer = MarianTokenizer.from_pretrained(config.MODEL_NAME)\n    model = MarianMTModel.from_pretrained(config.MODEL_NAME)\n    \n    print(f\"\\n💾 Saving model to {config.MODEL_SAVE_PATH}...\")\n    os.makedirs(config.MODEL_SAVE_PATH, exist_ok=True)\n    \n    tokenizer.save_pretrained(config.MODEL_SAVE_PATH)\n    model.save_pretrained(config.MODEL_SAVE_PATH)\n    \n    print(\"\\n✅ Model downloaded and saved successfully!\")\n    print(\"\\n\" + \"=\"*80)\n    print(\"NEXT STEPS:\")\n    print(\"=\"*80)\n    print(\"1. Click 'Save Version' in Kaggle\")\n    print(\"2. Go to Output tab after notebook finishes\")\n    print(\"3. Click 'New Dataset' to create a dataset from saved_model folder\")\n    print(\"4. Name it (e.g., 'marian-mt-model')\")\n    print(\"5. Add this dataset as input to your notebook\")\n    print(\"6. Update MODEL_DATASET_PATH in Config\")\n    print(\"7. Set DOWNLOAD_MODE = False\")\n    print(\"8. Turn off internet and run again!\")\n    print(\"=\"*80)\n    \n    return tokenizer, model\n\n# ============================================================================\n# Model Loading from Dataset (STEP 2)\n# ============================================================================\ndef load_model_from_dataset(config):\n    \"\"\"Load model from Kaggle dataset (offline mode)\"\"\"\n    print(\"=\"*80)\n    print(\"STEP 2: LOADING MODEL FROM KAGGLE DATASET\")\n    print(\"=\"*80)\n    \n    if not os.path.exists(config.MODEL_DATASET_PATH):\n        raise FileNotFoundError(\n            f\"\\n❌ Model dataset not found at: {config.MODEL_DATASET_PATH}\\n\\n\"\n            \"Please complete STEP 1 first:\\n\"\n            \"1. Set DOWNLOAD_MODE = True\\n\"\n            \"2. Turn ON internet\\n\"\n            \"3. Run notebook to download model\\n\"\n            \"4. Create Kaggle dataset from output\\n\"\n            \"5. Add dataset to notebook inputs\\n\"\n            \"6. Update MODEL_DATASET_PATH\\n\"\n            \"7. Set DOWNLOAD_MODE = False\"\n        )\n    \n    print(f\"📂 Loading from: {config.MODEL_DATASET_PATH}\")\n    \n    tokenizer = MarianTokenizer.from_pretrained(config.MODEL_DATASET_PATH)\n    model = MarianMTModel.from_pretrained(config.MODEL_DATASET_PATH)\n    \n    print(\"✅ Model loaded successfully from dataset!\")\n    return tokenizer, model\n\n# ============================================================================\n# Data Loading Functions\n# ============================================================================\ndef load_data(config):\n    \"\"\"Load and prepare training data\"\"\"\n    train_df = pd.read_csv(os.path.join(config.DATA_PATH, 'train.csv'))\n    train_df = train_df.dropna(subset=['transliteration', 'translation'])\n    \n    src_texts = train_df['transliteration'].values\n    tgt_texts = train_df['translation'].values\n    \n    src_train, src_val, tgt_train, tgt_val = train_test_split(\n        src_texts, tgt_texts, \n        test_size=config.VAL_SPLIT, \n        random_state=config.RANDOM_STATE\n    )\n    \n    return src_train, src_val, tgt_train, tgt_val\n\ndef create_data_loaders(src_train, src_val, tgt_train, tgt_val, tokenizer, config):\n    \"\"\"Create PyTorch data loaders\"\"\"\n    train_dataset = TranslationDataset(\n        src_train, tgt_train, tokenizer, config.MAX_LENGTH\n    )\n    val_dataset = TranslationDataset(\n        src_val, tgt_val, tokenizer, config.MAX_LENGTH\n    )\n    \n    train_loader = DataLoader(\n        train_dataset, \n        batch_size=config.BATCH_SIZE, \n        shuffle=True\n    )\n    val_loader = DataLoader(\n        val_dataset, \n        batch_size=config.BATCH_SIZE, \n        shuffle=False\n    )\n    \n    return train_loader, val_loader\n\n# ============================================================================\n# Main Execution\n# ============================================================================\ndef main():\n    \"\"\"Main training pipeline\"\"\"\n    config = Config()\n    print(f\"Using device: {config.DEVICE}\\n\")\n    \n    # Load model based on mode\n    if config.DOWNLOAD_MODE:\n        # STEP 1: Download and save model\n        tokenizer, model = download_and_save_model(config)\n        print(\"\\n⚠️  STOP HERE! Follow the instructions above before continuing.\")\n        return\n    else:\n        # STEP 2: Load from dataset (offline mode)\n        try:\n            tokenizer, model = load_model_from_dataset(config)\n        except FileNotFoundError as e:\n            print(e)\n            return\n        except Exception as e:\n            print(f\"\\n❌ Error: {e}\")\n            print(\"\\n💡 Make sure:\")\n            print(\"1. You've completed STEP 1 (download mode)\")\n            print(\"2. Created a Kaggle dataset from the saved model\")\n            print(\"3. Added that dataset to this notebook's inputs\")\n            print(\"4. Updated MODEL_DATASET_PATH in the Config class\")\n            return\n    \n    # Continue with training...\n    print(\"\\nLoading data...\")\n    src_train, src_val, tgt_train, tgt_val = load_data(config)\n    print(f\"Training samples: {len(src_train)}, Validation samples: {len(src_val)}\")\n    \n    train_loader, val_loader = create_data_loaders(\n        src_train, src_val, tgt_train, tgt_val, tokenizer, config\n    )\n    \n    optimizer = AdamW(model.parameters(), lr=config.LEARNING_RATE)\n    total_steps = len(train_loader) * config.EPOCHS\n    scheduler = get_linear_schedule_with_warmup(\n        optimizer,\n        num_warmup_steps=config.WARMUP_STEPS,\n        num_training_steps=total_steps\n    )\n    \n    trainer = Trainer(model, tokenizer, config)\n    \n    print(\"\\nStarting training...\")\n    best_val_loss = float('inf')\n    \n    for epoch in range(config.EPOCHS):\n        print(f\"\\n{'='*80}\")\n        print(f\"Epoch {epoch + 1}/{config.EPOCHS}\")\n        print(f\"{'='*80}\")\n        \n        train_loss = trainer.train_epoch(train_loader, optimizer, scheduler)\n        val_loss = trainer.evaluate(val_loader)\n        \n        print(f\"\\nTrain Loss: {train_loss:.4f} | Val Loss: {val_loss:.4f}\")\n        \n        if val_loss < best_val_loss:\n            best_val_loss = val_loss\n            torch.save(model.state_dict(), 'best_model.pt')\n            print(\"✓ Saved best model\")\n    \n    print(\"\\nLoading best model for predictions...\")\n    model.load_state_dict(torch.load('best_model.pt'))\n    \n    print(\"\\nGenerating test predictions...\")\n    test_df = pd.read_csv(os.path.join(config.DATA_PATH, 'test.csv'))\n    src_test = test_df['transliteration'].values\n    \n    test_dataset = TranslationDataset(\n        src_test, None, tokenizer, config.MAX_LENGTH\n    )\n    test_loader = DataLoader(\n        test_dataset, \n        batch_size=config.BATCH_SIZE, \n        shuffle=False\n    )\n    \n    predictions = trainer.predict(test_loader)\n    \n    submission = pd.DataFrame({\n        'id': test_df['id'],\n        'translation': predictions\n    })\n    submission.to_csv('submission.csv', index=False)\n    \n    print(\"\\n\" + \"=\"*80)\n    print(\"✅ SUBMISSION SAVED to 'submission.csv'\")\n    print(\"=\"*80)\n    print(\"\\nSample predictions:\")\n    print(submission.head(10))\n    \n    print(\"\\n\" + \"=\"*80)\n    print(\"Example Translations:\")\n    print(\"=\"*80)\n    for i in range(min(5, len(test_df))):\n        print(f\"\\n{i+1}. Source: {src_test[i]}\")\n        print(f\"   Translation: {predictions[i]}\")\n\nif __name__ == '__main__':\n    main()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-24T17:42:26.460841Z","iopub.execute_input":"2025-12-24T17:42:26.461485Z","iopub.status.idle":"2025-12-24T17:44:19.164446Z","shell.execute_reply.started":"2025-12-24T17:42:26.461461Z","shell.execute_reply":"2025-12-24T17:44:19.163729Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}