{"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":[{"sourceType":"competition","sourceId":121150,"databundleVersionId":15745837},{"sourceType":"datasetVersion","sourceId":14410665,"datasetId":9203761,"databundleVersionId":15226414},{"sourceType":"datasetVersion","sourceId":14376272,"datasetId":9181082,"databundleVersionId":15188111},{"sourceType":"datasetVersion","sourceId":14374989,"datasetId":9147887,"databundleVersionId":15186684},{"sourceType":"modelInstanceVersion","sourceId":282751,"databundleVersionId":11375937,"modelInstanceId":239470,"modelId":222398}],"dockerImageVersionId":31236,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport pandas as pd\nimport torch\nfrom torch.utils.data import DataLoader, Dataset\nfrom transformers import AutoTokenizer, AutoModelForSeq2SeqLM\nfrom tqdm.auto import tqdm\nimport re\nimport warnings\nwarnings.filterwarnings('ignore')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T13:15:34.915680Z","iopub.execute_input":"2026-02-19T13:15:34.915860Z","iopub.status.idle":"2026-02-19T13:15:49.039215Z","shell.execute_reply.started":"2026-02-19T13:15:34.915841Z","shell.execute_reply":"2026-02-19T13:15:49.038661Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Load Models","metadata":{}},{"cell_type":"code","source":"CONFIG = {\n    \"data_path\": \"/kaggle/input/deep-past-initiative-machine-translation/test.csv\",\n    \"models\": [\n        \"/kaggle/input/byt5-base-big-data2\",\n        \"/kaggle/input/byt5-akkadian-model\", \n        \"/kaggle/input/train-gap-all-2/byt5-base-akkadian_gap_setence2\"\n    ],\n    # Model 2 (byt5-akkadian-model) gets dominant weight\n    \"model_weights\": [0.30, 0.55, 0.15],\n    \"device\": torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\"),\n    \"max_len\": 512,\n    \"batch_size\": 8,\n    \"gen_params\": {\n        \"num_beams\": 12,              # Higher exploration\n        \"max_new_tokens\": 512,\n        \"length_penalty\": 1.15,       # Slight preference for complete sentences\n        \"early_stopping\": True,\n        \"no_repeat_ngram_size\": 3     # Prevents repetition during generation\n    }\n}\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T13:15:49.100592Z","iopub.execute_input":"2026-02-19T13:15:49.100921Z","iopub.status.idle":"2026-02-19T13:15:49.122600Z","shell.execute_reply.started":"2026-02-19T13:15:49.100890Z","shell.execute_reply":"2026-02-19T13:15:49.121994Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Preprocess & Post-Process","metadata":{}},{"cell_type":"code","source":"def preprocess_transliteration(text):\n    \"\"\"Follows official gap/determinative guidelines.\"\"\"\n    if pd.isna(text): return \"\"\n    \n    s = str(text)\n    \n    # Fix determinatives: (d) → {d} per host recommendation\n    s = s.replace('(d)', '{d}')\n    \n    # Normalize gaps per official docs\n    # Big gaps first: multiple x, …, […] sequences\n    s = re.sub(r'(x{2,}|[…]{1,}|\\[[^\\]]*?\\]){1,}', ' <big_gap> ', s)\n    # Single gaps\n    s = re.sub(r'\\bx\\b|\\[x\\]', ' <gap> ', s)\n    \n    # Final cleanup\n    s = re.sub(r'\\s+', ' ', s).strip()\n    return s\n\ndef postprocess_translation(text):\n    \"\"\"Minimal: Only fix model artifacts.\"\"\"\n    if not isinstance(text, str) or not text.strip(): return \"\"\n    \n    # 1. Normalize special chars\n    text = text.replace('ḫ', 'h').replace('Ḫ', 'H')\n    text = text.replace('š', 's').replace('Š', 'S')\n    \n    # 2. Remove model artifacts (brackets)\n    text = re.sub(r'\\[.*?\\]', '', text)\n    text = re.sub(r'\\(.*?\\)', '', text)\n    \n    # 3. Fractions (safe to keep)\n    frac_map = {r'\\.5\\b': ' ½', r'\\.25\\b': ' ¼', r'\\.75\\b': ' ¾'}\n    for pat, rep in frac_map.items():\n        text = re.sub(r'(\\d+)' + pat, r'\\1' + rep, text)\n    \n    # 4. Clean whitespace\n    return re.sub(r'\\s+', ' ', text).strip()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T13:16:10.222061Z","iopub.execute_input":"2026-02-19T13:16:10.222968Z","iopub.status.idle":"2026-02-19T13:16:10.230020Z","shell.execute_reply.started":"2026-02-19T13:16:10.222931Z","shell.execute_reply":"2026-02-19T13:16:10.229316Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Weightings","metadata":{}},{"cell_type":"code","source":"def create_model_soup():\n    \"\"\"Weighted parameter averaging from reference.\"\"\"\n    total_score = sum(CONFIG['model_weights'])\n    WEIGHTS = [w / total_score for w in CONFIG['model_weights']]\n    print(f\"Using weights: {[f'{w:.3f}' for w in WEIGHTS]}\")\n    \n    # Template: Model 2 (best)\n    template_model = AutoModelForSeq2SeqLM.from_pretrained(\n        CONFIG['models'][1], local_files_only=True\n    )\n    soup_sd = template_model.state_dict()\n    \n    # Load others\n    model_1_sd = AutoModelForSeq2SeqLM.from_pretrained(\n        CONFIG['models'][0], local_files_only=True\n    ).state_dict()\n    \n    model_3_sd = AutoModelForSeq2SeqLM.from_pretrained(\n        CONFIG['models'][2], local_files_only=True\n    ).state_dict()\n    \n    # Average\n    for key in soup_sd:\n        weighted_value = WEIGHTS[1] * soup_sd[key]\n        norm_factor = WEIGHTS[1]\n        \n        if key in model_1_sd:\n            weighted_value += WEIGHTS[0] * model_1_sd[key]\n            norm_factor += WEIGHTS[0]\n        if key in model_3_sd:\n            weighted_value += WEIGHTS[2] * model_3_sd[key]\n            norm_factor += WEIGHTS[2]\n            \n        soup_sd[key] = weighted_value / norm_factor\n    \n    template_model.load_state_dict(soup_sd)\n    return template_model.to(CONFIG['device']).eval().float()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T13:16:29.480523Z","iopub.execute_input":"2026-02-19T13:16:29.481014Z","iopub.status.idle":"2026-02-19T13:16:29.487123Z","shell.execute_reply.started":"2026-02-19T13:16:29.480988Z","shell.execute_reply":"2026-02-19T13:16:29.486438Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Inference","metadata":{}},{"cell_type":"code","source":"class AkkadianTranslationDataset(Dataset):\n    def __init__(self, dataframe):\n        self.ids = dataframe['id'].tolist()\n        self.texts = [\n            \"translate Akkadian to English: \" + preprocess_transliteration(t) \n            for t in dataframe['transliteration']\n        ]\n    def __len__(self): return len(self.ids)\n    def __getitem__(self, idx): return self.ids[idx], self.texts[idx]\n\n# Load data\ndataframe = pd.read_csv(CONFIG['data_path'])\n\nmodel = create_model_soup()\ntokenizer = AutoTokenizer.from_pretrained(CONFIG['models'][1], local_files_only=True)\n\ndata_loader = DataLoader(\n    AkkadianTranslationDataset(dataframe),\n    batch_size=CONFIG['batch_size'],\n    shuffle=False,\n    num_workers=0,\n    collate_fn=lambda batch: (\n        [item[0] for item in batch],\n        tokenizer([item[1] for item in batch], \n                 max_length=CONFIG['max_len'], \n                 padding=True, truncation=True, \n                 return_tensors=\"pt\")\n    )\n)\n\n# Inference\ninference_results = []\nwith torch.inference_mode():\n    for ids, inputs in tqdm(data_loader):\n        outputs = model.generate(\n            input_ids=inputs.input_ids.to(CONFIG['device']),\n            attention_mask=inputs.attention_mask.to(CONFIG['device']),\n            **CONFIG['gen_params']\n        )\n        decoded = tokenizer.batch_decode(outputs, skip_special_tokens=True)\n        cleaned = [postprocess_translation(t) for t in decoded]\n        inference_results.extend(zip(ids, cleaned))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T13:16:41.490978Z","iopub.execute_input":"2026-02-19T13:16:41.491703Z","iopub.status.idle":"2026-02-19T13:17:46.406688Z","shell.execute_reply.started":"2026-02-19T13:16:41.491676Z","shell.execute_reply":"2026-02-19T13:17:46.405902Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Submission","metadata":{}},{"cell_type":"code","source":"submission_df = pd.DataFrame(inference_results, columns=['id', 'translation'])\nsubmission_df.to_csv(\"submission.csv\", index=False)\nprint(\"Sample outputs:\")\nprint(submission_df.head().to_string(index=False))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T13:17:46.407983Z","iopub.execute_input":"2026-02-19T13:17:46.408524Z","iopub.status.idle":"2026-02-19T13:17:46.423236Z","shell.execute_reply.started":"2026-02-19T13:17:46.408501Z","shell.execute_reply":"2026-02-19T13:17:46.422606Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}