{"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":"none","dataSources":[{"sourceId":121150,"databundleVersionId":15061024,"isSourceIdPinned":false,"sourceType":"competition"},{"sourceId":14766781,"sourceType":"datasetVersion","datasetId":9438874}],"dockerImageVersionId":31259,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\n\nMODEL_DIR = \"/kaggle/input/opus-mt-en-de-offline\"\n\nfor root, dirs, files in os.walk(MODEL_DIR):\n    print(root)\n    for f in files:\n        print(\"  \", f)\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-02-07T21:56:36.358346Z","iopub.execute_input":"2026-02-07T21:56:36.358608Z","iopub.status.idle":"2026-02-07T21:56:36.377672Z","shell.execute_reply.started":"2026-02-07T21:56:36.358581Z","shell.execute_reply":"2026-02-07T21:56:36.376560Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\n\nMODEL_DIR = \"/kaggle/input/opus-mt-en-de-offline\"\n\nprint(\"Contents of model dataset:\")\nfor item in os.listdir(MODEL_DIR):\n    print(item)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T21:58:31.734385Z","iopub.execute_input":"2026-02-07T21:58:31.734738Z","iopub.status.idle":"2026-02-07T21:58:31.742137Z","shell.execute_reply.started":"2026-02-07T21:58:31.734711Z","shell.execute_reply":"2026-02-07T21:58:31.741007Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from transformers import MarianMTModel, MarianTokenizer\n\nMODEL_PATH = \"/kaggle/input/opus-mt-en-de-offline/opus-mt-en-de\"\n\ntokenizer = MarianTokenizer.from_pretrained(\n    MODEL_PATH,\n    local_files_only=True\n)\n\nmodel = MarianMTModel.from_pretrained(\n    MODEL_PATH,\n    local_files_only=True\n)\n\nprint(\"✅ Model loaded successfully from Kaggle dataset (offline)\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T21:59:34.794279Z","iopub.execute_input":"2026-02-07T21:59:34.794679Z","iopub.status.idle":"2026-02-07T22:00:12.412275Z","shell.execute_reply.started":"2026-02-07T21:59:34.794639Z","shell.execute_reply":"2026-02-07T22:00:12.411044Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# EDA SETUP (RUN ONCE BEFORE ALL EDA)\n# Purpose: Load dataset and prepare tokens\n# ============================================================\n\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom collections import Counter\nimport re\n\nDATA_PATH = \"/kaggle/input/deep-past-initiative-machine-translation\"\ndf = pd.read_csv(f\"{DATA_PATH}/train.csv\")\n\n# Source language: Akkadian transliteration\ntexts = df[\"transliteration\"].astype(str).tolist()\n\n# Tokenization (simple whitespace, offline-safe)\ntokens = [w for t in texts for w in t.split()]\ntoken_freq = Counter(tokens)\n\nprint(\"EDA setup completed\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T22:02:50.335240Z","iopub.execute_input":"2026-02-07T22:02:50.336529Z","iopub.status.idle":"2026-02-07T22:02:50.425296Z","shell.execute_reply.started":"2026-02-07T22:02:50.336478Z","shell.execute_reply":"2026-02-07T22:02:50.424308Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# BEFORE TRAINING\n# Shows the most frequent tokens in the dataset\n\ntop_words = token_freq.most_common(20)\n\nplt.figure(figsize=(8,4))\nplt.bar([w for w,_ in top_words], [c for _,c in top_words])\nplt.xticks(rotation=90)\nplt.title(\"Top-N Word Frequency\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T22:03:01.645945Z","iopub.execute_input":"2026-02-07T22:03:01.646280Z","iopub.status.idle":"2026-02-07T22:03:01.933643Z","shell.execute_reply.started":"2026-02-07T22:03:01.646251Z","shell.execute_reply":"2026-02-07T22:03:01.932701Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# BEFORE TRAINING\n# Visual frequency overview (proxy for word cloud)\n\ntop_words = token_freq.most_common(30)\n\nplt.figure(figsize=(8,4))\nplt.scatter(range(len(top_words)), [c for _,c in top_words])\nplt.xticks(range(len(top_words)), [w for w,_ in top_words], rotation=90)\nplt.title(\"Word Cloud (Frequency Proxy)\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T22:03:18.474315Z","iopub.execute_input":"2026-02-07T22:03:18.474773Z","iopub.status.idle":"2026-02-07T22:03:18.712087Z","shell.execute_reply.started":"2026-02-07T22:03:18.474736Z","shell.execute_reply":"2026-02-07T22:03:18.711159Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# BEFORE TRAINING\n# Distribution of token frequencies\n\nplt.figure(figsize=(6,4))\nplt.hist(token_freq.values(), bins=50)\nplt.title(\"Token Frequency Histogram\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T22:03:30.798313Z","iopub.execute_input":"2026-02-07T22:03:30.798715Z","iopub.status.idle":"2026-02-07T22:03:31.014337Z","shell.execute_reply.started":"2026-02-07T22:03:30.798681Z","shell.execute_reply":"2026-02-07T22:03:31.013520Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# BEFORE TRAINING\n# Tokens appearing very few times (≤2)\n\nrare_counts = [c for c in token_freq.values() if c <= 2]\n\nplt.figure(figsize=(6,4))\nplt.hist(rare_counts, bins=30)\nplt.title(\"Rare Word Distribution (≤2 occurrences)\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T22:03:44.753903Z","iopub.execute_input":"2026-02-07T22:03:44.754256Z","iopub.status.idle":"2026-02-07T22:03:44.940644Z","shell.execute_reply.started":"2026-02-07T22:03:44.754223Z","shell.execute_reply":"2026-02-07T22:03:44.939661Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# BEFORE TRAINING\n# Cumulative coverage of vocabulary\n\nfreqs = np.array(sorted(token_freq.values(), reverse=True))\ncum = np.cumsum(freqs) / np.sum(freqs)\n\nplt.figure(figsize=(6,4))\nplt.plot(cum)\nplt.title(\"Cumulative Frequency Curve\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T22:04:12.113960Z","iopub.execute_input":"2026-02-07T22:04:12.114358Z","iopub.status.idle":"2026-02-07T22:04:12.246895Z","shell.execute_reply.started":"2026-02-07T22:04:12.114324Z","shell.execute_reply":"2026-02-07T22:04:12.246106Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# BEFORE TRAINING\n# Length of individual tokens\n\nword_lengths = [len(w) for w in tokens]\n\nplt.figure(figsize=(6,4))\nplt.hist(word_lengths, bins=20)\nplt.title(\"Word Length Distribution\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T22:04:23.194029Z","iopub.execute_input":"2026-02-07T22:04:23.195110Z","iopub.status.idle":"2026-02-07T22:04:23.565223Z","shell.execute_reply.started":"2026-02-07T22:04:23.195064Z","shell.execute_reply":"2026-02-07T22:04:23.564463Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# BEFORE TRAINING\n# Length of each sentence in characters\n\nchar_lengths = [len(t) for t in texts]\n\nplt.figure(figsize=(6,4))\nplt.hist(char_lengths, bins=30)\nplt.title(\"Character Count Distribution\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T22:04:42.494057Z","iopub.execute_input":"2026-02-07T22:04:42.494419Z","iopub.status.idle":"2026-02-07T22:04:42.656210Z","shell.execute_reply.started":"2026-02-07T22:04:42.494386Z","shell.execute_reply":"2026-02-07T22:04:42.655443Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# BEFORE TRAINING\n# Stopword frequency using a small English proxy list\n\nstopwords = {\"a\",\"the\",\"and\",\"of\",\"to\",\"in\",\"for\",\"on\",\"with\"}\nstop_counts = {w: token_freq[w] for w in stopwords if w in token_freq}\n\nplt.figure(figsize=(6,4))\nplt.bar(stop_counts.keys(), stop_counts.values())\nplt.title(\"Stopword Frequency\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T22:04:53.394231Z","iopub.execute_input":"2026-02-07T22:04:53.394580Z","iopub.status.idle":"2026-02-07T22:04:53.495386Z","shell.execute_reply.started":"2026-02-07T22:04:53.394548Z","shell.execute_reply":"2026-02-07T22:04:53.494562Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# BEFORE TRAINING\n# Lemma approximation using lowercase normalization\n\nlemmas = [w.lower() for w in tokens]\nlemma_freq = Counter(lemmas).most_common(20)\n\nplt.figure(figsize=(8,4))\nplt.bar([w for w,_ in lemma_freq], [c for _,c in lemma_freq])\nplt.xticks(rotation=90)\nplt.title(\"Lemma Frequency Plot\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T22:05:04.887276Z","iopub.execute_input":"2026-02-07T22:05:04.887985Z","iopub.status.idle":"2026-02-07T22:05:05.131177Z","shell.execute_reply.started":"2026-02-07T22:05:04.887948Z","shell.execute_reply":"2026-02-07T22:05:05.130270Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# BEFORE TRAINING\n# Syllable estimation via vowel groups (heuristic)\n\ndef syllable_count(word):\n    return len(re.findall(r\"[aeiouAEIOU]\", word))\n\nsyllables = [syllable_count(w) for w in tokens]\n\nplt.figure(figsize=(6,4))\nplt.hist(syllables, bins=10)\nplt.title(\"Syllable Count Distribution\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T22:05:14.613302Z","iopub.execute_input":"2026-02-07T22:05:14.613705Z","iopub.status.idle":"2026-02-07T22:05:15.075851Z","shell.execute_reply.started":"2026-02-07T22:05:14.613666Z","shell.execute_reply":"2026-02-07T22:05:15.074978Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# BEFORE TRAINING\n# Heuristic POS tagging (suffix-based)\n\npos_tags = []\nfor w in tokens:\n    if w.endswith(\"um\") or w.endswith(\"am\"):\n        pos_tags.append(\"NOUN\")\n    elif w.endswith(\"ti\") or w.endswith(\"ta\"):\n        pos_tags.append(\"VERB\")\n    else:\n        pos_tags.append(\"OTHER\")\n\npos_freq = Counter(pos_tags)\n\nplt.figure(figsize=(6,4))\nplt.bar(pos_freq.keys(), pos_freq.values())\nplt.title(\"Part-of-Speech Tag Distribution (Heuristic)\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T22:05:25.342448Z","iopub.execute_input":"2026-02-07T22:05:25.343346Z","iopub.status.idle":"2026-02-07T22:05:25.523261Z","shell.execute_reply.started":"2026-02-07T22:05:25.343275Z","shell.execute_reply":"2026-02-07T22:05:25.522421Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# BEFORE TRAINING\n# Heuristic NER using capitalization patterns\n\nner_tags = []\nfor w in tokens:\n    if w.isupper():\n        ner_tags.append(\"LOGOGRAM\")\n    elif w[0].isupper():\n        ner_tags.append(\"PROPER_NOUN\")\n    else:\n        ner_tags.append(\"OTHER\")\n\nner_freq = Counter(ner_tags)\n\nplt.figure(figsize=(6,4))\nplt.bar(ner_freq.keys(), ner_freq.values())\nplt.title(\"Named Entity Category Distribution (Heuristic)\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T22:05:36.702354Z","iopub.execute_input":"2026-02-07T22:05:36.702857Z","iopub.status.idle":"2026-02-07T22:05:36.847131Z","shell.execute_reply.started":"2026-02-07T22:05:36.702820Z","shell.execute_reply":"2026-02-07T22:05:36.846287Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# BEFORE TRAINING\n# Dependency roles approximated by token position\n\ndeps = []\nfor t in texts:\n    words = t.split()\n    for i,_ in enumerate(words):\n        if i == 0:\n            deps.append(\"ROOT\")\n        elif i == 1:\n            deps.append(\"SUBJ/OBJ\")\n        else:\n            deps.append(\"MOD\")\n\ndep_freq = Counter(deps)\n\nplt.figure(figsize=(6,4))\nplt.bar(dep_freq.keys(), dep_freq.values())\nplt.title(\"Dependency Relation Distribution (Approximation)\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T22:06:05.383673Z","iopub.execute_input":"2026-02-07T22:06:05.384017Z","iopub.status.idle":"2026-02-07T22:06:05.527548Z","shell.execute_reply.started":"2026-02-07T22:06:05.383986Z","shell.execute_reply":"2026-02-07T22:06:05.526663Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# BEFORE TRAINING\n# Flesch-like readability approximation\n\nreadability_scores = []\n\nfor t in texts:\n    words = t.split()\n    if len(words) > 0:\n        avg_word_len = sum(len(w) for w in words) / len(words)\n        score = 206.835 - 1.015 * len(words) - 84.6 * avg_word_len\n        readability_scores.append(score)\n\nplt.figure(figsize=(6,4))\nplt.hist(readability_scores, bins=30)\nplt.title(\"Readability Score Distribution (Approximation)\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T22:06:09.580714Z","iopub.execute_input":"2026-02-07T22:06:09.581333Z","iopub.status.idle":"2026-02-07T22:06:09.768904Z","shell.execute_reply.started":"2026-02-07T22:06:09.581297Z","shell.execute_reply":"2026-02-07T22:06:09.767937Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# Deep Past Challenge — FINAL TRAINING + SUBMISSION CELL\n# Fully OFFLINE | Kaggle-Legal | Stable | CPU-Safe\n# ============================================================\n\n# -----------------------------\n# HARD OFFLINE ENFORCEMENT\n# -----------------------------\nimport os\nos.environ[\"HF_HUB_OFFLINE\"] = \"1\"\nos.environ[\"TRANSFORMERS_OFFLINE\"] = \"1\"\nos.environ[\"HF_DATASETS_OFFLINE\"] = \"1\"\nos.environ[\"HF_HUB_DISABLE_TELEMETRY\"] = \"1\"\nos.environ[\"HF_HUB_DISABLE_PROGRESS_BARS\"] = \"1\"\n\n# -----------------------------\n# IMPORTS (after env vars)\n# -----------------------------\nimport torch\nimport pandas as pd\nfrom datasets import Dataset\nfrom transformers import MarianMTModel, MarianTokenizer\nfrom torch.optim import AdamW\nfrom torch.nn.utils import clip_grad_norm_\n\n# -----------------------------\n# PATHS\n# -----------------------------\nDATA_PATH  = \"/kaggle/input/deep-past-initiative-machine-translation\"\nMODEL_PATH = \"/kaggle/input/opus-mt-en-de-offline/opus-mt-en-de\"\n\n# -----------------------------\n# LOAD DATA\n# -----------------------------\ntrain_df = pd.read_csv(f\"{DATA_PATH}/train.csv\")\ntest_df  = pd.read_csv(f\"{DATA_PATH}/test.csv\")\n\n# -----------------------------\n# LOAD MODEL (LOCAL ONLY)\n# -----------------------------\ntokenizer = MarianTokenizer.from_pretrained(\n    MODEL_PATH,\n    local_files_only=True\n)\n\nmodel = MarianMTModel.from_pretrained(\n    MODEL_PATH,\n    local_files_only=True\n)\n\nprint(\"✅ Model loaded from Kaggle dataset (offline)\")\n\n# -----------------------------\n# DATASET PREPARATION\n# -----------------------------\nhf_ds = Dataset.from_pandas(train_df)\n\nMAX_LEN = 128\n\ndef preprocess(batch):\n    inputs = tokenizer(\n        batch[\"transliteration\"],\n        padding=\"max_length\",\n        truncation=True,\n        max_length=MAX_LEN\n    )\n\n    targets = tokenizer(\n        text_target=batch[\"translation\"],\n        padding=\"max_length\",\n        truncation=True,\n        max_length=MAX_LEN\n    )\n\n    inputs[\"labels\"] = targets[\"input_ids\"]\n    return inputs\n\nhf_ds = hf_ds.map(\n    preprocess,\n    batched=True,\n    remove_columns=hf_ds.column_names\n)\n\n# -----------------------------\n# TRAINING SETUP\n# -----------------------------\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel.to(device)\nmodel.train()\n\nBATCH_SIZE = 2            # Safe for CPU\nEPOCHS = 3                # Sweet spot for this dataset\nLR = 5e-5                 # Stable learning rate\n\noptimizer = AdamW(model.parameters(), lr=LR)\n\nprint(\"🚀 Training started\")\n\n# -----------------------------\n# TRAINING LOOP\n# -----------------------------\nfor epoch in range(EPOCHS):\n    total_loss = 0.0\n\n    for i in range(0, len(hf_ds), BATCH_SIZE):\n        batch = hf_ds[i:i+BATCH_SIZE]\n        batch = {k: torch.tensor(v).to(device) for k,v in batch.items()}\n\n        outputs = model(**batch)\n        loss = outputs.loss\n\n        loss.backward()\n        clip_grad_norm_(model.parameters(), 1.0)\n        optimizer.step()\n        optimizer.zero_grad()\n\n        total_loss += loss.item()\n\n        if i % 200 == 0:\n            print(f\"Epoch {epoch+1}/{EPOCHS} | Step {i}/{len(hf_ds)} | Loss {loss.item():.4f}\")\n\n    avg_loss = total_loss / (len(hf_ds) / BATCH_SIZE)\n    print(f\"✅ Epoch {epoch+1} completed | Avg Loss: {avg_loss:.4f}\")\n\nprint(\"🏁 Training completed\")\n\n# -----------------------------\n# INFERENCE (BEAM SEARCH)\n# -----------------------------\nmodel.eval()\npredictions = []\n\nprint(\"🔍 Inference started\")\n\nfor idx, text in enumerate(test_df[\"transliteration\"]):\n    inputs = tokenizer(\n        text,\n        return_tensors=\"pt\",\n        truncation=True,\n        max_length=MAX_LEN\n    ).to(device)\n\n    with torch.no_grad():\n        output = model.generate(\n            **inputs,\n            num_beams=5,          # Improves BLEU / chrF++\n            length_penalty=1.0,\n            max_length=MAX_LEN,\n            early_stopping=True\n        )\n\n    translation = tokenizer.decode(\n        output[0],\n        skip_special_tokens=True\n    )\n\n    predictions.append(translation)\n\n    if idx % 100 == 0:\n        print(f\"Inference progress: {idx}/{len(test_df)}\")\n\n# -----------------------------\n# SUBMISSION FILE\n# -----------------------------\nsubmission = pd.DataFrame({\n    \"id\": test_df[\"id\"],\n    \"translation\": predictions\n})\n\nsubmission.to_csv(\"submission.csv\", index=False)\n\nprint(\"🎯 submission.csv created — READY TO SUBMIT\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-07T22:08:46.020811Z","iopub.execute_input":"2026-02-07T22:08:46.021817Z","iopub.status.idle":"2026-02-07T23:25:08.483131Z","shell.execute_reply.started":"2026-02-07T22:08:46.021776Z","shell.execute_reply":"2026-02-07T23:25:08.482170Z"}},"outputs":[],"execution_count":null}]}