{"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":"\"\"\"\nKaggle-Safe Ultra Optimized Inference Engine\nFully offline compatible. Production-grade.\n\"\"\"\n\nimport os\nimport re\nimport gc\nimport random\nimport logging\nimport warnings\nfrom pathlib import Path\nfrom dataclasses import dataclass\nfrom typing import List\n\nimport numpy as np\nimport pandas as pd\nimport torch\nfrom torch.utils.data import Dataset, DataLoader, Sampler\nfrom torch.cuda.amp import autocast\nfrom transformers import AutoTokenizer, AutoModelForSeq2SeqLM\nfrom tqdm.auto import tqdm\n\nwarnings.filterwarnings(\"ignore\")\n\n# ============================================================\n# 🔒 Reproducibility\n# ============================================================\n\ndef seed_everything(seed: int = 42):\n    random.seed(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    torch.cuda.manual_seed_all(seed)\n\nseed_everything(42)\n\n# ============================================================\n# ⚙️ Config\n# ============================================================\n\n@dataclass\nclass Config:\n    test_data_path: str = \"/kaggle/input/deep-past-initiative-machine-translation/test.csv\"\n    model_path: str = \"/kaggle/input/datasets/assiaben/final-byt5/byt5-akkadian-optimized-34x\"\n    output_dir: str = \"/kaggle/working/\"\n\n    max_length: int = 512\n    max_new_tokens: int = 512\n\n    batch_size: int = 8\n    num_workers: int = 2\n\n    num_beams: int = 8\n    length_penalty: float = 1.5\n    early_stopping: bool = True\n\n    use_mixed_precision: bool = True\n    use_adaptive_beams: bool = True\n    use_bucket_batching: bool = True\n    num_buckets: int = 4\n\n    def __post_init__(self):\n        self.device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n        Path(self.output_dir).mkdir(parents=True, exist_ok=True)\n        if not torch.cuda.is_available():\n            self.use_mixed_precision = False\n\nconfig = Config()\n\nprint(f\"PyTorch: {torch.__version__}\")\nprint(f\"Device: {config.device}\")\n\n# ============================================================\n# 📝 Logging\n# ============================================================\n\nlogging.basicConfig(\n    level=logging.INFO,\n    format=\"%(asctime)s | %(levelname)s | %(message)s\"\n)\nlogger = logging.getLogger(__name__)\n\n# ============================================================\n# 🔄 Preprocessor\n# ============================================================\n\nclass Preprocessor:\n    def __init__(self):\n        self.big_gap = re.compile(r\"(\\.{3,}|…+)\")\n        self.small_gap = re.compile(r\"(xx+|\\s+x\\s+)\")\n\n    def process(self, texts: List[str]) -> List[str]:\n        s = pd.Series(texts).fillna(\"\").astype(str)\n        s = s.str.replace(self.big_gap, \"<big_gap>\", regex=True)\n        s = s.str.replace(self.small_gap, \"<gap>\", regex=True)\n        return (\"translate Akkadian to English: \" + s).tolist()\n\n# ============================================================\n# 🧹 Postprocessor\n# ============================================================\n\nclass Postprocessor:\n    def __init__(self):\n        self.whitespace = re.compile(r\"\\s+\")\n        self.repeated_words = re.compile(r\"\\b(\\w+)(?:\\s+\\1\\b)+\")\n\n    def process(self, texts: List[str]) -> List[str]:\n        s = pd.Series(texts).fillna(\"\").astype(str)\n        s = s.str.replace(self.whitespace, \" \", regex=True)\n        s = s.str.replace(self.repeated_words, r\"\\1\", regex=True)\n        return s.str.strip().tolist()\n\n# ============================================================\n# 📦 Dataset\n# ============================================================\n\nclass AkkadianDataset(Dataset):\n    def __init__(self, df: pd.DataFrame, preprocessor: Preprocessor):\n        self.ids = df[\"id\"].tolist()\n        self.texts = preprocessor.process(df[\"transliteration\"].tolist())\n\n    def __len__(self):\n        return len(self.ids)\n\n    def __getitem__(self, idx):\n        return self.ids[idx], self.texts[idx]\n\n# ============================================================\n# 🪣 Bucket Sampler\n# ============================================================\n\nclass BucketSampler(Sampler):\n    def __init__(self, dataset, batch_size, num_buckets):\n        lengths = [len(t.split()) for _, t in dataset]\n        sorted_idx = sorted(range(len(lengths)), key=lambda i: lengths[i])\n        bucket_size = len(sorted_idx) // num_buckets\n\n        self.batches = []\n        for i in range(num_buckets):\n            start = i * bucket_size\n            end = None if i == num_buckets - 1 else (i + 1) * bucket_size\n            bucket = sorted_idx[start:end]\n            for j in range(0, len(bucket), batch_size):\n                self.batches.append(bucket[j:j + batch_size])\n\n    def __iter__(self):\n        return iter(self.batches)\n\n    def __len__(self):\n        return len(self.batches)\n\n# ============================================================\n# 🧠 Inference Engine\n# ============================================================\n\nclass InferenceEngine:\n\n    def __init__(self, config: Config):\n        self.config = config\n        self.pre = Preprocessor()\n        self.post = Postprocessor()\n        self._load_model()\n\n    def _load_model(self):\n        logger.info(\"Loading model (offline local path)...\")\n\n        model_path = Path(self.config.model_path)\n\n        if not model_path.exists():\n            raise ValueError(f\"Model path not found: {model_path}\")\n\n        self.model = AutoModelForSeq2SeqLM.from_pretrained(\n            model_path,\n            local_files_only=True,      # 🔑 prevents HF repo validation\n            trust_remote_code=True\n        ).to(self.config.device).eval()\n\n        self.tokenizer = AutoTokenizer.from_pretrained(\n            model_path,\n            local_files_only=True,\n            trust_remote_code=True\n        )\n\n        total_params = sum(p.numel() for p in self.model.parameters())\n        logger.info(f\"Model loaded | Params: {total_params:,}\")\n\n    def _collate(self, batch):\n        ids = [b[0] for b in batch]\n        texts = [b[1] for b in batch]\n\n        tokens = self.tokenizer(\n            texts,\n            max_length=self.config.max_length,\n            padding=True,\n            truncation=True,\n            return_tensors=\"pt\"\n        )\n\n        return ids, tokens\n\n    def _adaptive_beams(self, attention_mask):\n        if not self.config.use_adaptive_beams:\n            return self.config.num_beams\n\n        lengths = attention_mask.sum(dim=1)\n        if lengths.float().mean() < 80:\n            return max(4, self.config.num_beams // 2)\n        return self.config.num_beams\n\n    def run(self, df: pd.DataFrame) -> pd.DataFrame:\n\n        dataset = AkkadianDataset(df, self.pre)\n\n        if self.config.use_bucket_batching:\n            sampler = BucketSampler(dataset, self.config.batch_size, self.config.num_buckets)\n            loader = DataLoader(\n                dataset,\n                batch_sampler=sampler,\n                num_workers=self.config.num_workers,\n                collate_fn=self._collate\n            )\n        else:\n            loader = DataLoader(\n                dataset,\n                batch_size=self.config.batch_size,\n                shuffle=False,\n                num_workers=self.config.num_workers,\n                collate_fn=self._collate\n            )\n\n        results = []\n\n        with torch.inference_mode():\n            for ids, tokens in tqdm(loader, desc=\"Translating\"):\n                input_ids = tokens.input_ids.to(self.config.device)\n                attention_mask = tokens.attention_mask.to(self.config.device)\n\n                beam_size = self._adaptive_beams(attention_mask)\n\n                gen_kwargs = dict(\n                    max_new_tokens=self.config.max_new_tokens,\n                    num_beams=beam_size,\n                    length_penalty=self.config.length_penalty,\n                    early_stopping=self.config.early_stopping,\n                    use_cache=True\n                )\n\n                if self.config.use_mixed_precision:\n                    with autocast():\n                        outputs = self.model.generate(\n                            input_ids=input_ids,\n                            attention_mask=attention_mask,\n                            **gen_kwargs\n                        )\n                else:\n                    outputs = self.model.generate(\n                        input_ids=input_ids,\n                        attention_mask=attention_mask,\n                        **gen_kwargs\n                    )\n\n                decoded = self.tokenizer.batch_decode(\n                    outputs, skip_special_tokens=True\n                )\n\n                cleaned = self.post.process(decoded)\n                results.extend(zip(ids, cleaned))\n\n                del input_ids, attention_mask, outputs\n                torch.cuda.empty_cache()\n\n        gc.collect()\n\n        return pd.DataFrame(results, columns=[\"id\", \"translation\"])\n\n# ============================================================\n# 🏁 Run\n# ============================================================\n\nlogger.info(\"Loading test data...\")\ntest_df = pd.read_csv(config.test_data_path)\n\nengine = InferenceEngine(config)\nsubmission = engine.run(test_df)\n\noutput_path = Path(config.output_dir) / \"submission.csv\"\nsubmission.to_csv(output_path, index=False)\n\nprint(\"\\nSubmission Preview:\")\nprint(submission.head())\nprint(f\"\\nSaved to: {output_path}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-13T05:55:52.355177Z","iopub.execute_input":"2026-02-13T05:55:52.355536Z","iopub.status.idle":"2026-02-13T05:56:15.300365Z","shell.execute_reply.started":"2026-02-13T05:55:52.3555Z","shell.execute_reply":"2026-02-13T05:56:15.299093Z"}},"outputs":[],"execution_count":null}]}