{"cells": [{"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# --- RUN 21: EXACT LOCAL REPLICA (FP32 + Manual Device) ---\n# STRATEGY: \n# 1. Install PEFT 0.18.0 (Proven Required).\n# 2. Use FP32 (Local used FP32). FP16 might be unstable for ByT5.\n# 3. Use Manual .to(device) (Local used this). device_map='auto' might cause issues.\nimport sys\nimport os\nimport subprocess\n\ndef log(msg):\n    print(msg, flush=True)\n\nlog(\"--- RUN 21 START: FP32 Exact Replica ---\")\n\n# 1. OFFLINE INSTALLATION\nwheel_path = \"/kaggle/input/peft-0-18-0-wheel/peft-0.18.0-py3-none-any.whl\"\nlog(f\"Installing PEFT from: {wheel_path}\")\n\ntry:\n    if os.path.exists(wheel_path):\n        cmd = [sys.executable, \"-m\", \"pip\", \"install\", \"--no-index\", \"--no-deps\", \"--force-reinstall\", wheel_path]\n        subprocess.check_call(cmd)\n        log(\"PEFT Installation successful.\")\n    else:\n        log(\"CRITICAL: Wheel not found. Using Find Links fallback.\")\n        cmd = [sys.executable, \"-m\", \"pip\", \"install\", \"--no-index\", \"--no-deps\", \"--find-links=/kaggle/input/peft-0-18-0-wheel\", \"peft\"]\n        subprocess.check_call(cmd)\nexcept Exception as e:\n    log(f\"INSTALL ERROR: {e}\")\n"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "import traceback\nimport pandas as pd\nimport re\nimport torch\nimport gc\nfrom transformers import AutoTokenizer, AutoModelForSeq2SeqLM\nfrom peft import PeftModel\nfrom tqdm.auto import tqdm\n\n# --- CONFIGURATION ---\nADAPTER_PATH = \"./assyrian_byt5_qlora_v2_refined\"\nOUTPUT_FILE = \"submission.csv\"\nDEVICE = \"cuda\" if torch.cuda.is_available() else \"cpu\"\nlog(f\"Device: {DEVICE}\")\n\n# Check Imports\nimport peft\nlog(f\"PEFT Version: {peft.__version__}\")\n"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# --- SETUP PATHS ---\nlog(\"Looking for dataset & inputs...\")\ninput_root = \"/kaggle/input/\"\ntarget_folder = \"./assyrian_byt5_qlora_v2_refined\"\nTEST_FILE = None\nBASE_MODEL_PATH = None\n\ntry:\n    for root, dirs, files in os.walk(input_root):\n        if \"test.csv\" in files and \"deep-past\" in root:\n            TEST_FILE = os.path.join(root, \"test.csv\")\n            break\n        elif \"test.csv\" in files:\n            TEST_FILE = os.path.join(root, \"test.csv\")\n\n    if not TEST_FILE:\n        TEST_FILE = \"test.csv\"\n        pd.DataFrame({'id': [1], 'transliteration': ['szumma a-wi-lum']}).to_csv(TEST_FILE, index=False)\n\n    source_adapter_dir = None\n    for root, dirs, files in os.walk(input_root):\n        if \"adapter_model.safetensors\" in files or \"adapter_config.json\" in files:\n            if \"checkpoint\" not in root:\n                source_adapter_dir = root\n                break\n    if source_adapter_dir:\n        import shutil\n        if os.path.exists(target_folder):\n            shutil.rmtree(target_folder)\n        shutil.copytree(source_adapter_dir, target_folder)\n\n    for root, dirs, files in os.walk(input_root):\n        if \"config.json\" in files:\n             path_lower = root.lower()\n             if \"byt5\" in path_lower and \"adapter\" not in path_lower and \"checkpoint\" not in path_lower:\n                 BASE_MODEL_PATH = root\n                 break\n    \n    if not BASE_MODEL_PATH:\n         BASE_MODEL_PATH = \"google/byt5-base\" \n\nexcept Exception as e:\n    log(\"CRITICAL SETUP ERROR\")\n    traceback.print_exc()\n"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "# --- HELPER FUNCTIONS ---\ndef clean_input_transliteration(text):\n    if not isinstance(text, str): return \"\"\n    text = text.replace('\\u0161', 'sz').replace('\\u0160', 'SZ')\n    text = text.replace('\\u1e63', 's,').replace('\\u1e62', 'S,')\n    text = text.replace('\\u1e6d', 't,').replace('\\u1e6c', 'T,')\n    subscript_map = str.maketrans(\"\\u2080\\u2081\\u2082\\u2083\\u2084\\u2085\\u2086\\u2087\\u2088\\u2089\", \"0123456789\")\n    text = text.translate(subscript_map)\n    text = re.sub(r'[!?/{}]', '', text)\n    text = text.replace('[', '').replace(']', '')\n    text = re.sub(r'^\\d+[\\'\\\"]?\\.?\\s*', '', text)\n    return text.strip()\n\ndef surgical_loop_fix(text):\n    if not isinstance(text, str): return \"\"\n    loop_pattern = re.compile(r'(.{4,})\\1\\1+')\n    match = loop_pattern.search(text)\n    if match:\n        start, end = match.span()\n        clean_text = text[:start] + match.group(1)\n        return clean_text.strip()\n    text = re.sub(r'(\\b\\w+\\s+)\\1{2,}', r'\\1', text)\n    return text\n"}, {"cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": "try:\n    log(f\"Loading Base Model from: {BASE_MODEL_PATH}...\")\n    tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL_PATH)\n    \n    log(\"Loading Model in FP32 (Explicitly No Device Map)...\")\n    # EXACT MATCH of local `generate_submission.py` logic\n    # 1. Load Base Model (Defaults to FP32)\n    model = AutoModelForSeq2SeqLM.from_pretrained(BASE_MODEL_PATH)\n    \n    # 2. Attach Adapter\n    log(f\"Loading Platinum Adapter: {ADAPTER_PATH}...\")\n    model = PeftModel.from_pretrained(model, ADAPTER_PATH)\n    \n    # 3. Move to Device\n    log(\"Moving to Device...\")\n    model.to(DEVICE)\n    model.eval()\n    \n    # --- DATA LOADING ---\n    log(f\"Loading test data from {TEST_FILE}...\")\n    df_test = pd.read_csv(TEST_FILE)\n    \n    col = 'transliteration' if 'transliteration' in df_test.columns else 'Transliteration'\n    inputs_list = df_test[col].apply(clean_input_transliteration).tolist()\n\n    log(\"Running Inference...\")\n    predictions = []\n    batch_size = 8 \n    \n    for i in tqdm(range(0, len(inputs_list), batch_size)):\n        batch_texts = inputs_list[i : i + batch_size]\n        batch_texts = [\"Translate Assyrian to English: \" + str(t) for t in batch_texts]\n        inputs = tokenizer(batch_texts, return_tensors=\"pt\", padding=True, truncation=True, max_length=128).to(DEVICE)\n        \n        with torch.no_grad():\n            outputs = model.generate(**inputs, max_length=128, num_beams=5, early_stopping=True)\n        \n        decoded = tokenizer.batch_decode(outputs, skip_special_tokens=True)\n        predictions.extend(decoded)\n        \n        if i % 100 == 0:\n             gc.collect()\n             if torch.cuda.is_available(): torch.cuda.empty_cache()\n\n    df_test['translation'] = predictions\n    df_test['translation'] = df_test['translation'].apply(surgical_loop_fix)\n    df_test['translation'] = df_test['translation'].apply(lambda x: x[0].upper() + x[1:] if len(x) > 0 else x)\n\n    df_test[['id', 'translation']].to_csv(OUTPUT_FILE, index=False)\n    log(f\"SUCCESS: Submission generated at {OUTPUT_FILE}\")\n    \nexcept Exception as e:\n    log(\"FATAL ERROR IN PIPELINE\")\n    traceback.print_exc()\n"}], "metadata": {"kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"}}, "nbformat": 4, "nbformat_minor": 4}