{"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":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":121150,"databundleVersionId":15833391},{"sourceType":"datasetVersion","sourceId":14899351,"datasetId":9366631,"databundleVersionId":15763950},{"sourceType":"modelInstanceVersion","sourceId":755680,"databundleVersionId":15750522,"modelInstanceId":577094,"modelId":589419},{"sourceType":"kernelVersion","sourceId":174396613},{"sourceType":"kernelVersion","sourceId":294739278}],"dockerImageVersionId":31260,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"\"\"\"\nASSIA'S MODEL + CHUNKING + HYBRID MBR + BF16\nExpected Score: 35.4+\n\"\"\"\n# 1. Install offline dependencies using your attached package manager dataset\n!bash /kaggle/input/notebooks/packagemanager/pm-109400332-at-02-19-2026-08-37-17/install_requirements.sh\n\n# 2. Imports\nimport re\nimport pandas as pd\nimport torch\nfrom transformers import AutoTokenizer, AutoModelForSeq2SeqLM\nfrom tqdm.auto import tqdm\nfrom contextlib import nullcontext\nimport sacrebleu","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T18:31:38.951489Z","iopub.execute_input":"2026-02-19T18:31:38.952485Z","iopub.status.idle":"2026-02-19T18:32:02.304431Z","shell.execute_reply.started":"2026-02-19T18:31:38.952445Z","shell.execute_reply":"2026-02-19T18:32:02.303604Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# CONFIGURATION\n# ============================================================\nCONFIG = {\n    \"test_path\": \"/kaggle/input/competitions/deep-past-initiative-machine-translation/test.csv\",\n    \n    # Using the high-performing Model B exclusively\n    \"model_path\": \"/kaggle/input/models/mattiaangeli/byt5-akkadian-mbr-v2/pytorch/default/1\",\n    \n    \"device\": torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\"),\n    \n    \"max_length\": 512,  # <--- Added this back!\n    \n    \"generation\": {\n        \"num_beams\": 8,\n        \"max_new_tokens\": 512,\n        \"length_penalty\": 1.5,\n        \"early_stopping\": True\n    },\n    \n    # Minimum Bayes Risk (MBR) Settings\n    \"mbr\": {\n        \"num_beam_cands\": 4,     # 4 safe candidates\n        \"num_sample_cands\": 2,   # 2 diverse candidates\n        \"top_p\": 0.9,\n        \"temperature\": 0.7\n    }\n}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T18:38:02.361887Z","iopub.execute_input":"2026-02-19T18:38:02.36223Z","iopub.status.idle":"2026-02-19T18:38:02.368131Z","shell.execute_reply.started":"2026-02-19T18:38:02.362201Z","shell.execute_reply":"2026-02-19T18:38:02.367326Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# MBR (Minimum Bayes Risk) Utilities\n# ============================================================\nCHRF_METRIC = sacrebleu.metrics.CHRF(word_order=2)\n\ndef dedup_keep_order(xs):\n    seen = set()\n    out = []\n    for x in xs:\n        x = str(x).strip()\n        if x and x not in seen:\n            out.append(x)\n            seen.add(x)\n    return out\n\ndef sim_chrfpp(a: str, b: str) -> float:\n    a = (a or \"\").strip()\n    b = (b or \"\").strip()\n    if not a or not b: return 0.0\n    return float(CHRF_METRIC.sentence_score(a, [b]).score)\n\ndef mbr_pick(cands: list) -> str:\n    cands = dedup_keep_order(cands)\n    n = len(cands)\n    if n == 0: return \"\"\n    if n == 1: return cands[0]\n    \n    # Score each candidate against all others and pick the most \"central\" one\n    best_i, best_s = 0, -1e9\n    for i in range(n):\n        s = 0.0\n        for j in range(n):\n            if i == j: continue\n            s += sim_chrfpp(cands[i], cands[j])\n        s /= max(1, n - 1)\n        if s > best_s:\n            best_s, best_i = s, i\n            \n    return cands[int(best_i)]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T18:32:51.964686Z","iopub.execute_input":"2026-02-19T18:32:51.965446Z","iopub.status.idle":"2026-02-19T18:32:51.972326Z","shell.execute_reply.started":"2026-02-19T18:32:51.965415Z","shell.execute_reply":"2026-02-19T18:32:51.971637Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import re\nimport pandas as pd\n\n# ============================================================\n# 1. INPUT PREPROCESSING\n# ============================================================\ndef preprocess_input(text):\n    \"\"\"Minimal preprocessing for test data before the model sees it.\"\"\"\n    if pd.isna(text):\n        return \"\"\n    text = str(text)\n    # Standardize gaps\n    text = re.sub(r'(\\.{3,}|…+)', '<big_gap>', text)\n    text = re.sub(r'(xx+|\\s+x\\s+)', '<gap>', text)\n    return text\n\n# ============================================================\n# 2. CLAUSE CHUNKING (Splits Long Texts)\n# ============================================================\nCLAUSE_MARKERS = [\n    r'KIŠIB\\s+', r'IGI\\s+', r'um-ma\\s+', r'a-na\\s+\\S+\\s+qí-bi',\n    r'šu-ma\\s+', r'\\.\\s+', r'\\[\\.\\.\\.\\]\\s*'\n]\nCLAUSE_PATTERN = re.compile('|'.join(CLAUSE_MARKERS), re.IGNORECASE)\n\ndef split_akkadian(text, max_words=30, min_words=15):\n    \"\"\"Splits long texts so ByT5 doesn't get confused.\"\"\"\n    words = text.split()\n    if len(words) <= 50: return [text]\n    \n    chunks, current_chunk = [], []\n    for word in words:\n        current_chunk.append(word)\n        chunk_text = ' '.join(current_chunk)\n        is_break = bool(CLAUSE_PATTERN.search(chunk_text + ' '))\n        \n        if len(current_chunk) >= min_words and is_break:\n            chunks.append(chunk_text.strip())\n            current_chunk = []\n        elif len(current_chunk) >= max_words:\n            chunks.append(chunk_text.strip())\n            current_chunk = []\n            \n    if current_chunk: chunks.append(' '.join(current_chunk).strip())\n    return chunks\n\n# ============================================================\n# 3. AGGRESSIVE POST-PROCESSING (Renamed to match loop)\n# ============================================================\ndef remove_phrase_repeats(text):\n    \"\"\"Removes recursive phrases like 'of the city of the city'.\"\"\"\n    if not text: return text\n    words = text.split()\n    if len(words) < 6: return text\n    \n    for phrase_len in range(8, 2, -1):\n        i = 0\n        result_words = []\n        while i < len(words):\n            if i + phrase_len * 2 <= len(words):\n                phrase = words[i:i + phrase_len]\n                next_phrase = words[i + phrase_len:i + phrase_len * 2]\n                if phrase == next_phrase:\n                    result_words.extend(phrase)\n                    j = i + phrase_len\n                    while j + phrase_len <= len(words) and words[j:j + phrase_len] == phrase:\n                        j += phrase_len\n                    i = j\n                    continue\n            result_words.append(words[i])\n            i += 1\n        words = result_words\n    return ' '.join(words)\n\ndef aggressive_postprocess(text):  # <--- RENAMED FROM postprocess_output\n    \"\"\"Clean model output with strict formatting rules.\"\"\"\n    if not isinstance(text, str) or not text.strip():\n        return \"\"\n\n    # 1. Basic Char Normalization\n    text = text.replace('ḫ', 'h').replace('Ḫ', 'H')\n    text = text.translate(str.maketrans(\"₀₁₂₃₄₅₆₇₈₉\", \"0123456789\"))\n\n    # 2. Strict Gap Handling\n    text = re.sub(r'(\\.{3,}|…|\\[\\.+\\])', '<big_gap>', text)\n    text = re.sub(r'(\\[x\\]|\\(x\\)|\\bx\\b)', '<gap>', text, flags=re.I)\n    \n    # 3. Remove Scribal Annotations\n    text = re.sub(r'\\((fem|plur|pl|sing|\\?|!)\\.?\\)', '', text, flags=re.I)\n\n    # 4. Protect Tags\n    text = text.replace('<gap>', '\\x00GAP\\x00').replace('<big_gap>', '\\x00BIG\\x00')\n    forbidden = '!?()\"—–<>⌈⌋⌊[]+ʾ/;'\n    text = text.translate(str.maketrans('', '', forbidden))\n    text = text.replace('\\x00GAP\\x00', ' <gap> ').replace('\\x00BIG\\x00', ' <big_gap> ')\n\n    # 5. FRACTIONS (Compact Format: \"10½\")\n    text = re.sub(r'(\\d+)\\.5\\b', r'\\1½', text)\n    text = re.sub(r'\\b0\\.5\\b', '½', text)\n    text = re.sub(r'(\\d+)\\.25\\b', r'\\1¼', text)\n    text = re.sub(r'\\b0\\.25\\b', '¼', text)\n    text = re.sub(r'(\\d+)\\.75\\b', r'\\1¾', text)\n    text = re.sub(r'\\b0\\.75\\b', '¾', text)\n\n    # 6. Remove Repeats\n    text = re.sub(r'\\b(\\w+)(?:\\s+\\1\\b)+', r'\\1', text)\n    text = remove_phrase_repeats(text)\n\n    # 7. Final Polish\n    text = re.sub(r'\\s+', ' ', text).strip()\n    \n    return text","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T18:32:55.069602Z","iopub.execute_input":"2026-02-19T18:32:55.070386Z","iopub.status.idle":"2026-02-19T18:32:55.093006Z","shell.execute_reply.started":"2026-02-19T18:32:55.070341Z","shell.execute_reply":"2026-02-19T18:32:55.092107Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"🚀 Running Model B Inference (mbr-v2 + MBR + NO Chunking)...\")\n\nfinal_predictions = []\ntest_df = pd.read_csv(CONFIG['test_path'])\n\n# 1. LOAD MODEL B\ntokenizer = AutoTokenizer.from_pretrained(CONFIG['model_path']) \nmodel = AutoModelForSeq2SeqLM.from_pretrained(CONFIG['model_path']).to(CONFIG['device']).eval()\n\n# Auto-detect BF16 support (Crucial for doing whole-text translation without OOM)\nuse_bf16 = False\nif CONFIG['device'].type == \"cuda\":\n    try:\n        use_bf16 = torch.cuda.is_bf16_supported()\n    except Exception:\n        pass\nprint(f\"BF16 Autocast Enabled: {use_bf16}\")\n\nfor _, row in tqdm(test_df.iterrows(), total=len(test_df)):\n    res_id = row['id']\n    \n    # Preprocess the entire tablet text at once (NO CHUNKING)\n    original_text = preprocess_input(row['transliteration']) \n    input_text = \"translate Akkadian to English: \" + original_text\n    \n    inputs = tokenizer(input_text, return_tensors=\"pt\", max_length=CONFIG['max_length'], truncation=True).to(CONFIG['device'])\n    \n    # Adaptive Beams based on full text length\n    seq_len = inputs.attention_mask.sum().item()\n    beam_size = CONFIG['generation']['num_beams'] if seq_len >= 100 else max(4, CONFIG['generation']['num_beams'] // 2)\n    \n    pools = []\n    ctx = torch.autocast(device_type=\"cuda\", dtype=torch.bfloat16) if use_bf16 else nullcontext()\n    \n    with torch.inference_mode(), ctx:\n        \n        # -- 1. Generate Safe Candidates (Beam Search) --\n        beam_out = model.generate(\n            **inputs, \n            do_sample=False, \n            num_beams=beam_size, \n            num_return_sequences=CONFIG['mbr']['num_beam_cands'],\n            max_new_tokens=CONFIG['generation']['max_new_tokens'],\n            length_penalty=CONFIG['generation']['length_penalty'],\n            early_stopping=CONFIG['generation']['early_stopping'],\n            use_cache=True\n        )\n        pools.extend(tokenizer.batch_decode(beam_out, skip_special_tokens=True))\n        \n        # -- 2. Generate Diverse Candidates (Sampling) --\n        if CONFIG['mbr']['num_sample_cands'] > 0:\n            samp_out = model.generate(\n                **inputs, \n                do_sample=True, \n                num_beams=1, \n                top_p=CONFIG['mbr']['top_p'], \n                temperature=CONFIG['mbr']['temperature'],\n                num_return_sequences=CONFIG['mbr']['num_sample_cands'], \n                max_new_tokens=CONFIG['generation']['max_new_tokens'],\n                use_cache=True\n            )\n            pools.extend(tokenizer.batch_decode(samp_out, skip_special_tokens=True))\n            \n    # -- 3. Select Best Candidate via MBR (chrF++) --\n    best_raw_translation = mbr_pick(pools)\n    \n    # -- 4. Post-process the winning translation --\n    final_text = aggressive_postprocess(best_raw_translation)\n    \n    final_predictions.append({\"id\": res_id, \"translation\": final_text})\n\n# Export\nsubmission_df = pd.DataFrame(final_predictions)\nsubmission_df.to_csv(\"submission.csv\", index=False)\nprint(\"✅ Done! Saved submission.csv\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T18:38:08.868027Z","iopub.execute_input":"2026-02-19T18:38:08.868354Z","iopub.status.idle":"2026-02-19T18:38:39.002934Z","shell.execute_reply.started":"2026-02-19T18:38:08.868325Z","shell.execute_reply":"2026-02-19T18:38:39.002245Z"}},"outputs":[],"execution_count":null}]}