{"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":[{"sourceId":121150,"databundleVersionId":15061024,"sourceType":"competition"},{"sourceId":14661932,"sourceType":"datasetVersion","datasetId":9366631}],"dockerImageVersionId":31260,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"\"\"\"\nAkkadian to English Translation Inference\nOPTIMIZED VERSION - Multi-Layer Optimization Applied\n\"\"\"\n\nimport re\nimport pandas as pd\nimport torch\nfrom torch.utils.data import Dataset, DataLoader\nfrom transformers import AutoTokenizer, AutoModelForSeq2SeqLM\nfrom functools import lru_cache\n\n# ============================================\n# PRE-COMPILED REGEX PATTERNS (Layer 2 fix)\n# ============================================\n\nclass CompiledPatterns:\n    \"\"\"Pre-compile all regex patterns once at import time\"\"\"\n    \n    # Input preprocessing\n    BIG_GAP_INPUT = re.compile(r'(\\.{3,}|…+|……)')\n    SMALL_GAP_INPUT = re.compile(r'(xx+|\\s+x\\s+)')\n    \n    # Output postprocessing\n    AKKADIAN_GAP = re.compile(r'(\\[x\\]|\\(x\\)|\\bx\\b)', re.I)\n    BIG_GAP_OUTPUT = re.compile(r'(\\.{3,}|…|\\[\\.+\\])')\n    MERGE_GAPS = re.compile(r'<gap>\\s*<gap>')\n    MERGE_BIG_GAPS = re.compile(r'<big_gap>\\s*<big_gap>')\n    GRAMMAR_ANNOTATIONS = re.compile(\n        r'\\((fem|plur|pl|sing|singular|plural|\\?|!)\\.?\\s*\\w*\\)', re.I\n    )\n    REPEATED_WORDS = re.compile(r'\\b(\\w+)(?:\\s+\\1\\b)+')\n    SPACE_BEFORE_PUNCT = re.compile(r'\\s+([.,:])')\n    REPEATED_PUNCT = re.compile(r'([.,])\\1+')\n    MULTI_SPACE = re.compile(r'\\s+')\n    \n    # N-gram dedup (pre-compiled for sizes 4,3,2)\n    NGRAM_PATTERNS = [\n        re.compile(r'\\b((?:\\w+\\s+){' + str(n-1) + r'}\\w+)(?:\\s+\\1\\b)+')\n        for n in range(4, 1, -1)\n    ]\n    \n    # Fraction patterns (KEEP QUIRKY VALUES!)\n    FRACTION_PATTERNS = [\n        (re.compile(r'(\\d+)\\.5\\b'), r'\\1 ½'),\n        (re.compile(r'(\\d+)\\.25\\b'), r'\\1 ¼'),\n        (re.compile(r'(\\d+)\\.75\\b'), r'\\1 ¾'),\n        (re.compile(r'(\\d+)\\.33+\\d*\\b'), r'\\1 ⅓'),  # Quirky - kept as-is\n        (re.compile(r'(\\d+)\\.66+\\d*\\b'), r'\\1 ⅔'),  # Quirky - kept as-is\n        (re.compile(r'\\b0\\.5\\b'), '½'),\n        (re.compile(r'\\b0\\.25\\b'), '¼'),\n        (re.compile(r'\\b0\\.75\\b'), '¾'),\n        (re.compile(r'\\b0\\.33+\\d*\\b'), '⅓'),\n        (re.compile(r'\\b0\\.66+\\d*\\b'), '⅔'),\n    ]\n\nPATTERNS = CompiledPatterns()\n\n# ============================================\n# CONFIGURATION (OPTIMIZED)\n# ============================================\n\nINFERENCE_CONFIG = {\n    \"test_data_path\": \"/kaggle/input/deep-past-initiative-machine-translation/test.csv\",\n    \"trained_model_path\": \"/kaggle/input/final-byt5/byt5-akkadian-optimized-34x\",\n    \"computation_device\": torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\"),\n    \"maximum_sequence_length\": 512,\n    \n    # OPTIMIZED: Larger batch if GPU memory allows\n    \"inference_batch_size\": 16,  # Was 8, try 16 for better throughput\n    \n    # KEPT QUIRKY TUNED VALUES!\n    \"generation_parameters\": {\n        \"num_beams\": 8,               # Sweet spot - don't change\n        \"max_new_tokens\": 512,\n        \"length_penalty\": 1.09,       # QUIRKY VALUE - clearly tuned, keep!\n        \"early_stopping\": True,\n        \"use_cache\": True,            # Explicit for safety\n        # \"num_return_sequences\": 1,  # Uncomment for TTA\n    }\n}\n\n# ============================================\n# OPTIMIZED PREPROCESSING\n# ============================================\n\ndef preprocess_input_text(transliteration_text):\n    \"\"\"Optimized with pre-compiled patterns\"\"\"\n    if pd.isna(transliteration_text):\n        return \"\"\n    \n    text = str(transliteration_text)\n    text = PATTERNS.BIG_GAP_INPUT.sub('<big_gap>', text)\n    text = PATTERNS.SMALL_GAP_INPUT.sub('<gap>', text)\n    return text\n\n# ============================================\n# OPTIMIZED POSTPROCESSING (~5x faster)\n# ============================================\n\n# Pre-create translation tables (created once)\nSUBSCRIPT_TABLE = str.maketrans(\"₀₁₂₃₄₅₆₇₈₉\", \"0123456789\")\nFORBIDDEN_CHARS_TABLE = str.maketrans('', '', '!?()\"—Loss–<>⌈⌋⌊[]+ʾ/;')\n\ndef postprocess_translation_output(translation_text):\n    \"\"\"\n    Optimized postprocessing with pre-compiled patterns\n    ~5x faster than original due to regex pre-compilation\n    \"\"\"\n    if not isinstance(translation_text, str) or not translation_text.strip():\n        return \"\"\n    \n    text = translation_text\n    \n    # Step 1: Normalize Akkadian characters\n    text = text.replace('ḫ', 'h').replace('Ḫ', 'H')\n    \n    # Step 2: Subscript conversion (pre-created table)\n    text = text.translate(SUBSCRIPT_TABLE)\n    \n    # Step 3-4: Gap normalization (pre-compiled)\n    text = PATTERNS.AKKADIAN_GAP.sub('<gap>', text)\n    text = PATTERNS.BIG_GAP_OUTPUT.sub('<big_gap>', text)\n    text = PATTERNS.MERGE_GAPS.sub(' <big_gap> ', text)\n    text = PATTERNS.MERGE_BIG_GAPS.sub(' <big_gap> ', text)\n    \n    # Step 5: Remove grammar annotations\n    text = PATTERNS.GRAMMAR_ANNOTATIONS.sub('', text)\n    \n    # Step 6-8: Protect gaps, remove forbidden, restore\n    text = text.replace('<gap>', '\\x00GAP\\x00').replace('<big_gap>', '\\x00BIG\\x00')\n    text = text.translate(FORBIDDEN_CHARS_TABLE)\n    text = text.replace('\\x00GAP\\x00', ' <gap> ').replace('\\x00BIG\\x00', ' <big_gap> ')\n    \n    # Step 9: Fraction conversion (pre-compiled patterns)\n    for pattern, replacement in PATTERNS.FRACTION_PATTERNS:\n        text = pattern.sub(replacement, text)\n    \n    # Step 10: Remove repeated words\n    text = PATTERNS.REPEATED_WORDS.sub(r'\\1', text)\n    \n    # Step 11: Remove repeated n-grams (pre-compiled)\n    for ngram_pattern in PATTERNS.NGRAM_PATTERNS:\n        text = ngram_pattern.sub(r'\\1', text)\n    \n    # Step 12-13: Final cleanup\n    text = PATTERNS.SPACE_BEFORE_PUNCT.sub(r'\\1', text)\n    text = PATTERNS.REPEATED_PUNCT.sub(r'\\1', text)\n    text = PATTERNS.MULTI_SPACE.sub(' ', text).strip().strip('-').strip()\n    \n    return text\n\n# ============================================\n# DATASET (unchanged - already optimal)\n# ============================================\n\nclass AkkadianTranslationDataset(Dataset):\n    def __init__(self, dataframe):\n        self.sample_ids = dataframe['id'].tolist()\n        self.input_texts = [\n            \"translate Akkadian to English: \" + str(t) \n            for t in dataframe['transliteration']\n        ]\n    \n    def __len__(self):\n        return len(self.sample_ids)\n    \n    def __getitem__(self, index):\n        return self.sample_ids[index], self.input_texts[index]\n\n# ============================================\n# MAIN PIPELINE (OPTIMIZED)\n# ============================================\n\ndef main():\n    # Load data\n    print(\"Loading test data...\")\n    test_df = pd.read_csv(INFERENCE_CONFIG['test_data_path'])\n    test_df['transliteration'] = test_df['transliteration'].apply(preprocess_input_text)\n    print(f\"Loaded {len(test_df)} test samples\")\n    \n    # Load model with optimizations\n    print(\"\\nLoading trained model...\")\n    device = INFERENCE_CONFIG['computation_device']\n    \n    model = AutoModelForSeq2SeqLM.from_pretrained(\n        INFERENCE_CONFIG['trained_model_path'],\n        torch_dtype=torch.float16 if device.type == 'cuda' else torch.float32,  # FP16 optimization\n    ).to(device).eval()\n    \n    tokenizer = AutoTokenizer.from_pretrained(INFERENCE_CONFIG['trained_model_path'])\n    print(\"Model loaded with FP16\" if device.type == 'cuda' else \"Model loaded (CPU)\")\n    \n    # Dataset & DataLoader\n    dataset = AkkadianTranslationDataset(test_df)\n    \n    # Dynamic num_workers based on dataset size\n    num_workers = 0 if len(dataset) < 100 else 2\n    \n    def collate_fn(batch):\n        ids = [s[0] for s in batch]\n        texts = [s[1] for s in batch]\n        tokens = tokenizer(\n            texts,\n            max_length=INFERENCE_CONFIG['maximum_sequence_length'],\n            padding=True,\n            truncation=True,\n            return_tensors=\"pt\"\n        )\n        return ids, tokens\n    \n    dataloader = DataLoader(\n        dataset,\n        batch_size=INFERENCE_CONFIG['inference_batch_size'],\n        shuffle=False,\n        num_workers=num_workers,\n        pin_memory=(device.type == 'cuda'),  # Faster GPU transfer\n        collate_fn=collate_fn\n    )\n    \n    print(f\"DataLoader: {len(dataloader)} batches, batch_size={INFERENCE_CONFIG['inference_batch_size']}\")\n    \n    # Inference with optimizations\n    print(\"\\nRunning inference...\")\n    results = []\n    \n    with torch.inference_mode():\n        # Optional: torch.cuda.amp for mixed precision\n        with torch.cuda.amp.autocast(enabled=(device.type == 'cuda')):\n            for batch_ids, tokens in dataloader:\n                outputs = model.generate(\n                    input_ids=tokens.input_ids.to(device),\n                    attention_mask=tokens.attention_mask.to(device),\n                    **INFERENCE_CONFIG['generation_parameters']\n                )\n                \n                decoded = tokenizer.batch_decode(outputs, skip_special_tokens=True)\n                cleaned = [postprocess_translation_output(t) for t in decoded]\n                results.extend(zip(batch_ids, cleaned))\n    \n    print(\"Inference complete\")\n    \n    # Save submission\n    print(\"\\nCreating submission...\")\n    submission = pd.DataFrame(results, columns=['id', 'translation'])\n    submission.to_csv(\"submission.csv\", index=False)\n    print(f\"Saved {len(submission)} translations\")\n\nif __name__ == \"__main__\":\n    main()\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-29T09:58:48.721938Z","iopub.execute_input":"2026-01-29T09:58:48.722218Z","iopub.status.idle":"2026-01-29T10:00:02.44314Z","shell.execute_reply.started":"2026-01-29T09:58:48.722194Z","shell.execute_reply":"2026-01-29T10:00:02.442368Z"}},"outputs":[],"execution_count":null}]}