{"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":15833391},{"sourceType":"datasetVersion","sourceId":15118319,"datasetId":9680510,"databundleVersionId":16005765}],"dockerImageVersionId":31287,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Deep Past Initiative – Akkadian to English Machine Translation\n\nThis notebook implements a sequence-to-sequence neural machine translation system for translating Akkadian transliterations into English. \n\nWe fine-tune a pretrained ByT5 model using the HuggingFace `Seq2SeqTrainer` framework and evaluate performance using BLEU and chrF++ metrics. Final predictions are generated using beam search decoding and submitted in Kaggle format.","metadata":{"_uuid":"3d03a579-6140-482f-a9a4-9b207ea8d3ba","_cell_guid":"f5eb9e35-a4a6-40a2-b49f-fb2cc67c3330","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"!pip install sacrebleu","metadata":{"_uuid":"59d5ebcd-9064-42a1-981e-77e7f42eaf8f","_cell_guid":"c0fffd7d-e1c8-4624-b39a-d7f933ac3bad","trusted":true,"collapsed":false,"execution":{"iopub.status.busy":"2026-03-10T03:04:56.593717Z","iopub.execute_input":"2026-03-10T03:04:56.594037Z","iopub.status.idle":"2026-03-10T03:05:01.759066Z","shell.execute_reply.started":"2026-03-10T03:04:56.594010Z","shell.execute_reply":"2026-03-10T03:05:01.758082Z"},"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport torch\nimport gc\nimport re\n\nfrom datasets import Dataset\nfrom transformers import (\n    AutoTokenizer,\n    AutoModelForSeq2SeqLM,\n    DataCollatorForSeq2Seq,\n    Seq2SeqTrainingArguments,\n    Seq2SeqTrainer\n)\n\nimport sacrebleu\nimport math","metadata":{"_uuid":"57ebd772-337b-4aed-82c6-5d65af9eb883","_cell_guid":"f2fe233b-c18a-41a7-bee0-321d447a0cf8","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-03-10T03:05:01.761113Z","iopub.execute_input":"2026-03-10T03:05:01.761422Z","iopub.status.idle":"2026-03-10T03:05:24.319571Z","shell.execute_reply.started":"2026-03-10T03:05:01.761391Z","shell.execute_reply":"2026-03-10T03:05:24.318705Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Experiment Configuration\n\nWe define a centralized configuration class to store all hyperparameters and generation settings.\n\nThese settings are chosen to balance performance and GPU memory constraints.","metadata":{"_uuid":"ee468f0c-03e1-4dd7-8e03-997455aa8a91","_cell_guid":"8ba8ee36-fdee-4cee-9fac-ad7c0e217c27","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"class Config:\n    MODEL_NAME = \"google/byt5-base\"\n    MAX_LENGTH = 512\n    BATCH_SIZE = 8\n    GRAD_ACCUM = 2\n    EPOCHS = 50\n    LR = 5e-5\n\n    MAX_NEW_TOKENS = 512\n    NUM_BEAMS = 8\n\n    PREFIX = \"translate Akkadian to English: \"\n    \n    OUTPUT_DIR = \"/kaggle/working/csc753m-byt5-akkadian\"","metadata":{"_uuid":"6a8dba3d-fa89-43bc-bb14-d1a75e7887f7","_cell_guid":"d0eecead-14df-4d4f-ac7f-788c2a862447","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-03-10T03:05:24.320906Z","iopub.execute_input":"2026-03-10T03:05:24.321430Z","iopub.status.idle":"2026-03-10T03:05:24.326000Z","shell.execute_reply.started":"2026-03-10T03:05:24.321402Z","shell.execute_reply":"2026-03-10T03:05:24.325147Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Data Loading\n\nThe dataset is provided by the Deep Past Initiative Kaggle competition.\n\n- `train.csv` contains Akkadian transliterations and English translations.\n- `test.csv` contains transliterations only.","metadata":{"_uuid":"252af139-4fe4-489c-a10e-07e7e8d5930b","_cell_guid":"2ea497da-a700-4d17-a82d-530fe2e8826c","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"train_df = pd.read_csv(\"/kaggle/input/deep-past-initiative-machine-translation/train.csv\")","metadata":{"_uuid":"824c3d6a-902b-4662-a427-ecc34a092b08","_cell_guid":"e76563cb-87e1-4460-8cbd-833875e8f63c","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-03-10T03:05:24.327166Z","iopub.execute_input":"2026-03-10T03:05:24.327984Z","iopub.status.idle":"2026-03-10T03:05:24.385962Z","shell.execute_reply.started":"2026-03-10T03:05:24.327943Z","shell.execute_reply":"2026-03-10T03:05:24.385036Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Data Cleaning\n\nSome training examples contain multiple Akkadian lines paired with multi-sentence English translations.  \nSince the test set mainly contains single transliteration segments, we align the training data at the sentence level when possible.\n\nWe split English text by sentence punctuation and Akkadian text by newline.  \nIf the counts match, we treat them as one-to-one pairs. Otherwise, we keep the original example as a fallback.\n\nWe also remove empty, very short, and extremely long samples to reduce noise and improve training stability.","metadata":{"_uuid":"f2e2e062-54a2-41a1-8113-1c913cb494eb","_cell_guid":"30572c3c-0275-48ec-a0d3-416ef2585700","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"big_gap_pattern = re.compile(r\"(\\.{3,}|…+)\")\nsmall_gap_pattern = re.compile(r\"(xx+|\\s+x\\s+)\")\n\ndef preprocess_batch(texts):\n\n    s = pd.Series(texts).fillna(\"\").astype(str)\n\n    # normalize big gaps\n    s = s.str.replace(big_gap_pattern, \"<big_gap>\", regex=True)\n\n    # normalize small gaps\n    s = s.str.replace(small_gap_pattern, \"<gap>\", regex=True)\n\n    # normalize whitespace\n    s = s.str.replace(r\"\\s+\", \" \", regex=True)\n\n    return s.str.strip().tolist()","metadata":{"_uuid":"513c3ac6-f68e-45f9-b993-5817f36c4408","_cell_guid":"07d8e88e-caec-47d3-a1f7-317432f8f473","trusted":true,"collapsed":false,"execution":{"iopub.status.busy":"2026-03-10T03:05:24.388208Z","iopub.execute_input":"2026-03-10T03:05:24.388796Z","iopub.status.idle":"2026-03-10T03:05:24.395383Z","shell.execute_reply.started":"2026-03-10T03:05:24.388764Z","shell.execute_reply":"2026-03-10T03:05:24.394297Z"},"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pattern_gap = re.compile(r\"(\\[x\\]|\\(x\\)|\\bx\\b)\", re.I)\npattern_big_gap = re.compile(r\"(\\.{3,}|…)\")\n\npattern_repeated_words = re.compile(r\"\\b(\\w+)(?:\\s+\\1\\b)+\")\npattern_whitespace = re.compile(r\"\\s+\")\n\nsubscript_trans = str.maketrans(\"₀₁₂₃₄₅₆₇₈₉\", \"0123456789\")\n\nforbidden_chars = '!?()\"——<>⌈⌋⌊[]+ʾ/;'\nforbidden_trans = str.maketrans(\"\", \"\", forbidden_chars)\n\n\ndef postprocess_batch(translations):\n\n    s = pd.Series(translations).fillna(\"\").astype(str)\n\n    s = s.str.translate(subscript_trans)\n\n    s = s.str.replace(pattern_gap, \"<gap>\", regex=True)\n    s = s.str.replace(pattern_big_gap, \"<big_gap>\", regex=True)\n\n    s = s.str.translate(forbidden_trans)\n\n    s = s.str.replace(pattern_repeated_words, r\"\\1\", regex=True)\n\n    s = s.str.replace(pattern_whitespace, \" \", regex=True)\n\n    return s.str.strip().tolist()","metadata":{"_uuid":"bcee3f71-7e27-44ae-979d-39c06471f404","_cell_guid":"a76bf6b8-de9b-4075-a34c-8ab73f4cf0ff","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-03-10T03:05:24.396523Z","iopub.execute_input":"2026-03-10T03:05:24.396860Z","iopub.status.idle":"2026-03-10T03:05:24.412963Z","shell.execute_reply.started":"2026-03-10T03:05:24.396831Z","shell.execute_reply":"2026-03-10T03:05:24.411944Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Original training size:\", len(train_df))","metadata":{"_uuid":"1ce0b541-10d6-48c1-802e-91bbdd240693","_cell_guid":"2a2771dd-f2b3-4ee0-9162-97d1c273660f","trusted":true,"collapsed":false,"execution":{"iopub.status.busy":"2026-03-10T03:05:24.414275Z","iopub.execute_input":"2026-03-10T03:05:24.414711Z","iopub.status.idle":"2026-03-10T03:05:24.429407Z","shell.execute_reply.started":"2026-03-10T03:05:24.414672Z","shell.execute_reply":"2026-03-10T03:05:24.428366Z"},"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df[\"transliteration\"] = preprocess_batch(\n    train_df[\"transliteration\"].tolist()\n)\n\nprint(\"Cleaned data training size:\", len(train_df))","metadata":{"_uuid":"fe1bce15-0f65-46f6-9606-0bbb0c0904cc","_cell_guid":"0dc36087-49b2-4ced-b8c2-f5b858798183","trusted":true,"collapsed":false,"execution":{"iopub.status.busy":"2026-03-10T03:05:24.430694Z","iopub.execute_input":"2026-03-10T03:05:24.431140Z","iopub.status.idle":"2026-03-10T03:05:24.572336Z","shell.execute_reply.started":"2026-03-10T03:05:24.431110Z","shell.execute_reply":"2026-03-10T03:05:24.571592Z"},"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Data Augmentation and Train/Test Splitting\n\nWe split the training data into:\n- 90% training\n- 10% validation\n\nThe validation split is used to monitor BLEU and chrF++ during training.","metadata":{"_uuid":"26a3f3db-7351-40bc-9b38-2b9571362812","_cell_guid":"a0e08c54-3cbc-4b48-8108-c74558a2b230","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"dataset = Dataset.from_pandas(train_df)\nsplit = dataset.train_test_split(test_size=0.1, seed=42)\ntrain_dataset = split[\"train\"]\nval_dataset = split[\"test\"]","metadata":{"_uuid":"c1d3487d-72cc-4eb4-a092-498ddc39faef","_cell_guid":"c78c363b-5329-4e70-9598-28510b30455a","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-03-10T03:05:24.573252Z","iopub.execute_input":"2026-03-10T03:05:24.573502Z","iopub.status.idle":"2026-03-10T03:05:24.621047Z","shell.execute_reply.started":"2026-03-10T03:05:24.573477Z","shell.execute_reply":"2026-03-10T03:05:24.620382Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Preprocessing\n\nThis helps guide the model toward the intended translation task.\n\nPreprocessing steps:\n1. Add task prefix to transliterations.\n2. Tokenize inputs.\n3. Tokenize target translations.\n4. Attach target token IDs as labels.\n\nTokenization uses truncation to control maximum sequence length.","metadata":{"_uuid":"897a1f93-5a33-4541-9c6b-58914f21a5b9","_cell_guid":"70a4b0c3-f684-473a-86ac-2226d25ce001","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"tokenizer = AutoTokenizer.from_pretrained(Config.MODEL_NAME)\n\ndef tokenize_function(examples):\n\n    inputs = [Config.PREFIX + x for x in examples[\"transliteration\"]]\n    targets = examples[\"translation\"]\n\n    model_inputs = tokenizer(\n        inputs,\n        max_length=Config.MAX_LENGTH,\n        truncation=True\n    )\n\n    labels = tokenizer(\n        targets,\n        max_length=Config.MAX_LENGTH,\n        truncation=True\n    )[\"input_ids\"]\n\n    # ignore padding in loss\n    labels = [\n        [(token if token != tokenizer.pad_token_id else -100) for token in seq]\n        for seq in labels\n    ]\n\n    model_inputs[\"labels\"] = labels\n\n    return model_inputs","metadata":{"_uuid":"7894a9fd-4a14-4e61-9be4-3ad20e62a288","_cell_guid":"772fc805-bd64-450f-8945-9c26be343b7d","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-03-10T03:05:24.622172Z","iopub.execute_input":"2026-03-10T03:05:24.622506Z","iopub.status.idle":"2026-03-10T03:05:25.513747Z","shell.execute_reply.started":"2026-03-10T03:05:24.622470Z","shell.execute_reply":"2026-03-10T03:05:25.513077Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dataset = train_dataset.map(\n    tokenize_function,\n    batched=True,\n    remove_columns=train_dataset.column_names\n)\n\nval_dataset = val_dataset.map(\n    tokenize_function,\n    batched=True,\n    remove_columns=val_dataset.column_names\n)\n\ntrain_dataset.set_format(\"torch\")\nval_dataset.set_format(\"torch\")","metadata":{"_uuid":"afa2038a-839b-4084-bf2c-5fe745ff1336","_cell_guid":"8d3dbfd0-c524-44de-b7ff-afc871721c74","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-03-10T03:05:25.514944Z","iopub.execute_input":"2026-03-10T03:05:25.515382Z","iopub.status.idle":"2026-03-10T03:06:55.978588Z","shell.execute_reply.started":"2026-03-10T03:05:25.515343Z","shell.execute_reply":"2026-03-10T03:06:55.977794Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Model Training\n\nWe fine-tune the pretrained ByT5 model using HuggingFace's `Seq2SeqTrainer`.\n\nTraining proceeds for multiple epochs to allow the model to adapt to the Akkadian-English translation task.","metadata":{"_uuid":"678009bb-813a-4fd3-93bb-41160cc6b107","_cell_guid":"9e2cd39a-568a-4cb8-ae3a-c3f3bc40cc32","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"gc.collect()\ntorch.cuda.empty_cache()\n\nmodel = AutoModelForSeq2SeqLM.from_pretrained(Config.MODEL_NAME)\n\n# IMPORTANT: memory optimizations\nmodel.config.use_cache = False\nmodel.gradient_checkpointing_enable()\n\nmodel.generation_config.update(\n    num_beams=Config.NUM_BEAMS,\n    no_repeat_ngram_size=3,\n    length_penalty=0.8,\n    early_stopping=True,\n    max_new_tokens=Config.MAX_NEW_TOKENS\n)\n\ndata_collator = DataCollatorForSeq2Seq(\n    tokenizer,\n    model=model\n)","metadata":{"_uuid":"9303e42d-6a3d-45f7-81c9-d6494ad38c6f","_cell_guid":"b3d3af4d-6972-4297-9ab3-ba4880fb5b11","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-03-10T03:06:55.985498Z","iopub.execute_input":"2026-03-10T03:06:55.986242Z","iopub.status.idle":"2026-03-10T03:07:05.438332Z","shell.execute_reply.started":"2026-03-10T03:06:55.986214Z","shell.execute_reply":"2026-03-10T03:07:05.434577Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"training_args = Seq2SeqTrainingArguments(\n    # core\n    output_dir=Config.OUTPUT_DIR,\n    num_train_epochs=Config.EPOCHS,\n    learning_rate=Config.LR,\n    weight_decay=0.01,\n\n    # batch\n    per_device_train_batch_size=Config.BATCH_SIZE,\n    per_device_eval_batch_size=Config.BATCH_SIZE,\n    gradient_accumulation_steps=Config.GRAD_ACCUM,\n\n    # eval & log\n    eval_strategy=\"epoch\",\n    logging_strategy=\"epoch\",\n\n    # saving\n    save_strategy=\"epoch\",\n    save_total_limit=1,\n    # save_only_model=True,\n\n    lr_scheduler_type=\"cosine\",\n\n    # generation\n    predict_with_generate=True,\n\n    fp16=False\n)","metadata":{"_uuid":"4940fec7-67e5-4c2d-ab17-e3918955e782","_cell_guid":"7ac479b4-1669-4d01-a649-83c8f2c3e1fb","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-03-10T03:07:05.445564Z","iopub.execute_input":"2026-03-10T03:07:05.446263Z","iopub.status.idle":"2026-03-10T03:07:05.842300Z","shell.execute_reply.started":"2026-03-10T03:07:05.446224Z","shell.execute_reply":"2026-03-10T03:07:05.839304Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"trainer = Seq2SeqTrainer(\n    model=model,\n    args=training_args,\n    train_dataset=train_dataset,\n    eval_dataset=val_dataset,\n    data_collator=data_collator,\n)","metadata":{"_uuid":"67efc6f1-ccc2-4e06-bf32-fff191920656","_cell_guid":"48e8928d-4796-4d95-9d63-9d4cdf211125","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-03-10T03:07:05.843362Z","iopub.execute_input":"2026-03-10T03:07:05.843783Z","iopub.status.idle":"2026-03-10T03:07:07.574745Z","shell.execute_reply.started":"2026-03-10T03:07:05.843699Z","shell.execute_reply":"2026-03-10T03:07:07.568668Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"trainer.train()","metadata":{"_uuid":"9c345d32-6a8a-4510-b6cd-8f7d649402a5","_cell_guid":"85e8b972-ec32-4a57-bb72-c626e7e40eff","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2026-03-10T03:07:07.575985Z","iopub.execute_input":"2026-03-10T03:07:07.576402Z","iopub.status.idle":"2026-03-10T03:37:02.301293Z","shell.execute_reply.started":"2026-03-10T03:07:07.576363Z","shell.execute_reply":"2026-03-10T03:37:02.300287Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Validation Performance\n\nAfter training, we evaluate on the validation set.\n\nReported metrics include:\n- Validation loss\n- BLEU score\n- chrF++ score\n- Geometric mean\n\nThese results indicate how well the model generalizes to unseen transliterations.","metadata":{"_uuid":"e4c9ad27-b8f4-4c06-a49a-9de2f4760434","_cell_guid":"cc197aad-d2d9-40a5-aaf6-b33efca47247","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"def compute_metrics(eval_pred):\n\n    preds, labels = eval_pred\n\n    if isinstance(preds, tuple):\n        preds = preds[0]\n\n    preds = np.where(preds != -100, preds, tokenizer.pad_token_id)\n    labels = np.where(labels != -100, labels, tokenizer.pad_token_id)\n\n    decoded_preds = tokenizer.batch_decode(preds, skip_special_tokens=True)\n    decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)\n\n    decoded_preds = postprocess_batch(decoded_preds)\n    decoded_labels = postprocess_batch(decoded_labels)\n\n    decoded_labels = [[l] for l in decoded_labels]\n\n    bleu = sacrebleu.corpus_bleu(decoded_preds, decoded_labels)\n    chrf = sacrebleu.corpus_chrf(decoded_preds, decoded_labels, word_order=2)\n\n    geo = math.sqrt(bleu.score * chrf.score)\n\n    return {\n        \"bleu\": bleu.score,\n        \"chrf++\": chrf.score,\n        \"geometric_mean\": geo\n    }","metadata":{"_uuid":"f8a20f28-47c5-4504-8d81-29059c3af186","_cell_guid":"ed3a9684-c75c-4601-9c0a-63ab132ee1c1","trusted":true,"collapsed":false,"execution":{"iopub.status.busy":"2026-03-10T03:37:02.302660Z","iopub.execute_input":"2026-03-10T03:37:02.303052Z","iopub.status.idle":"2026-03-10T03:37:02.314926Z","shell.execute_reply.started":"2026-03-10T03:37:02.303009Z","shell.execute_reply":"2026-03-10T03:37:02.312723Z"},"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"trainer.compute_metrics = compute_metrics\n\nmetrics = trainer.evaluate()\nprint(metrics)","metadata":{"_uuid":"4dcda09b-4443-4611-8b97-4bb3d44a68bb","_cell_guid":"a9ff4e73-64e8-4fee-b88a-b20fc64ecfaa","trusted":true,"collapsed":false,"execution":{"iopub.status.busy":"2026-03-10T03:37:02.316041Z","iopub.execute_input":"2026-03-10T03:37:02.316322Z","iopub.status.idle":"2026-03-10T04:01:21.760479Z","shell.execute_reply.started":"2026-03-10T03:37:02.316284Z","shell.execute_reply":"2026-03-10T04:01:21.759705Z"},"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for i in range(5):\n\n    sample = val_dataset[i]\n\n    input_text = tokenizer.decode(\n        sample[\"input_ids\"],\n        skip_special_tokens=True\n    )\n\n    ref = tokenizer.decode(\n        [x for x in sample[\"labels\"] if x != -100],\n        skip_special_tokens=True\n    )\n\n    inputs = tokenizer(input_text, return_tensors=\"pt\").to(model.device)\n\n    outputs = model.generate(\n        **inputs,\n        max_new_tokens=Config.MAX_NEW_TOKENS,\n        num_beams=Config.NUM_BEAMS\n    )\n\n    pred = tokenizer.decode(outputs[0], skip_special_tokens=True)\n\n    print(\"INPUT:\", input_text)\n    print(\"PRED :\", pred)\n    print(\"REF  :\", ref)\n    print()","metadata":{"_uuid":"615f9ccd-1701-4377-943f-1291544bca5c","_cell_guid":"80309da8-2db1-4828-92ef-01c09779b726","trusted":true,"collapsed":false,"execution":{"iopub.status.busy":"2026-03-10T04:29:13.751170Z","iopub.execute_input":"2026-03-10T04:29:13.751530Z","iopub.status.idle":"2026-03-10T04:30:17.648686Z","shell.execute_reply.started":"2026-03-10T04:29:13.751498Z","shell.execute_reply":"2026-03-10T04:30:17.647923Z"},"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"trainer.save_model(Config.OUTPUT_DIR)\ntokenizer.save_pretrained(Config.OUTPUT_DIR)\nprint(f\"Model saved to {Config.OUTPUT_DIR}\")","metadata":{"_uuid":"c3c9e390-a274-41ea-ae7e-9a5f8e8377f6","_cell_guid":"51c6a61d-ea8c-4948-a274-8c5d6c695838","trusted":true,"collapsed":false,"execution":{"iopub.status.busy":"2026-03-10T04:01:21.773942Z","iopub.status.idle":"2026-03-10T04:01:21.774203Z","shell.execute_reply.started":"2026-03-10T04:01:21.774079Z","shell.execute_reply":"2026-03-10T04:01:21.774096Z"},"jupyter":{"outputs_hidden":false}},"outputs":[],"execution_count":null}]}