{"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":14402437,"sourceType":"datasetVersion","datasetId":9198432},{"sourceId":14496458,"sourceType":"datasetVersion","datasetId":9259056},{"sourceId":14534477,"sourceType":"datasetVersion","datasetId":9282978}],"dockerImageVersionId":31260,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install sacrebleu openpyxl evaluate -q ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T10:56:56.273019Z","iopub.execute_input":"2026-01-19T10:56:56.273816Z","iopub.status.idle":"2026-01-19T10:57:01.129654Z","shell.execute_reply.started":"2026-01-19T10:56:56.273775Z","shell.execute_reply":"2026-01-19T10:57:01.129000Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\nos.environ[\"PYTORCH_CUDA_ALLOC_CONF\"] = \"expandable_segments:True\"\nos.environ[\"NCCL_P2P_DISABLE\"] = \"1\"\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-19T10:57:01.131209Z","iopub.execute_input":"2026-01-19T10:57:01.131431Z","iopub.status.idle":"2026-01-19T10:57:01.427322Z","shell.execute_reply.started":"2026-01-19T10:57:01.131406Z","shell.execute_reply":"2026-01-19T10:57:01.426597Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport gc\n\n# 释放内存\ndef cleanup():\n    gc.collect()\n    torch.cuda.empty_cache()\n    # 如果在多 GPU 环境下，还可以手动指定设备\n    if torch.cuda.is_available():\n        with torch.cuda.device(0):\n            torch.cuda.empty_cache()\n        with torch.cuda.device(1):\n            torch.cuda.empty_cache()\n\ncleanup()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T10:57:01.428260Z","iopub.execute_input":"2026-01-19T10:57:01.428648Z","iopub.status.idle":"2026-01-19T10:57:05.266337Z","shell.execute_reply.started":"2026-01-19T10:57:01.428615Z","shell.execute_reply":"2026-01-19T10:57:05.265502Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import torch\nimport re\nimport shutil\nfrom datasets import Dataset, concatenate_datasets\nfrom transformers import (\n    AutoTokenizer, \n    AutoModelForSeq2SeqLM, \n    Seq2SeqTrainingArguments, \n    Seq2SeqTrainer, \n    DataCollatorForSeq2Seq\n)\nimport evaluate","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T10:57:05.267378Z","iopub.execute_input":"2026-01-19T10:57:05.268198Z","iopub.status.idle":"2026-01-19T10:57:31.607394Z","shell.execute_reply.started":"2026-01-19T10:57:05.268173Z","shell.execute_reply":"2026-01-19T10:57:31.606863Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==========================================\n# 1. 严格同步预处理 (完全保留 V1 推理逻辑)\n# ==========================================\nASCII_TO_DIACRITIC = {\n    \"sz\": \"š\", \"SZ\": \"Š\", \"Sz\": \"Š\", \"sh\": \"š\", \"SH\": \"Š\", \"Sh\": \"Š\",\n    \"s,\": \"ṣ\", \"S,\": \"Ṣ\", \"t,\": \"ṭ\", \"T,\": \"Ṭ\", \"z,\": \"ẓ\", \"Z,\": \"Ẓ\",\n    \".s\": \"ṣ\", \".S\": \"Ṣ\", \".t\": \"ṭ\", \".T\": \"Ṭ\", \".z\": \"ẓ\", \".Z\": \"Ẓ\",\n    \"h,\": \"ḫ\", \"H,\": \"Ḫ\", \".h\": \"ḫ\", \".H\": \"Ḫ\", \"hh\": \"ḫ\", \"HH\": \"Ḫ\",\n    \"'\": \"ʾ\", \"s2\": \"š\", \"S2\": \"Š\", \"s3\": \"ś\", \"S3\": \"Ś\",\n}\nVOWEL_SUBSCRIPTS = {\n    \"a2\": \"á\", \"a3\": \"à\", \"e2\": \"é\", \"e3\": \"è\", \"i2\": \"í\", \"i3\": \"ì\", \"u2\": \"ú\", \"u3\": \"ù\",\n    \"A2\": \"Á\", \"A3\": \"À\", \"E2\": \"É\", \"E3\": \"È\", \"I2\": \"Í\", \"I3\": \"Ì\", \"U2\": \"Ú\", \"U3\": \"Ù\",\n}\n\ndef apply_v1_normalization(text):\n    text = str(text)\n    # 变音符号转换\n    for ascii_seq, diacritic in sorted(ASCII_TO_DIACRITIC.items(), key=lambda x: -len(x[0])):\n        text = text.replace(ascii_seq, diacritic)\n    for ascii_seq, diacritic in VOWEL_SUBSCRIPTS.items():\n        text = text.replace(ascii_seq, diacritic)\n    \n    # Gap 处理 (同步 V1 逻辑)\n    text = re.sub(r\"\\.\\.\\.+\", \" x x x \", text)\n    text = re.sub(r\"\\.\\.\", \" x \", text)\n    tokens = text.split()\n    res, i = [], 0\n    while i < len(tokens):\n        t = tokens[i]\n        if t.lower() == \"x\":\n            cnt = 1\n            while i + cnt < len(tokens) and tokens[i + cnt].lower() == \"x\": cnt += 1\n            res.append(\"<gap>\" if cnt == 1 else \"<big_gap>\")\n            i += cnt\n        else:\n            res.append(t)\n            i += 1\n    return \" \".join(res)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T10:57:31.609347Z","iopub.execute_input":"2026-01-19T10:57:31.610104Z","iopub.status.idle":"2026-01-19T10:57:31.618096Z","shell.execute_reply.started":"2026-01-19T10:57:31.610076Z","shell.execute_reply":"2026-01-19T10:57:31.617370Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==========================================\n# 2. 改进后的数据加载（单数据集版本）\n# ==========================================\ndef load_and_tag_data(file_path):\n    \"\"\"\n    加载并处理单数据集\n    :param file_path: 数据集路径 (支持 CSV 或 Excel)\n    \"\"\"\n    # 自动根据后缀名加载数据\ndef load_and_tag_data(original_train, new_train):\n    # Assyrian (CSV)\n    df_original = pd.read_csv(original_train)\n    #切割样本\n    df_original = df_original.sample(frac=0.5, random_state=44)\n    # Babylonian (Excel)\n    df_new = pd.read_excel(new_train)\n    df_new = df_new.rename(columns={'source_text': 'transliteration', 'target_text': 'translation'})\n    \n    \n    def process_df(df):\n        rows = []\n        for _, row in df.iterrows():\n            src = apply_v1_normalization(row['transliteration'])\n            rows.append({\n                \"input_text\": \"translate Akkadian to English: \" + src,\n                \"target_text\": str(row['translation']).strip()\n            })\n        return rows\n\n    mixed_ds = Dataset.from_list(process_df(df_original) + process_df(df_new))\n    return mixed_ds.shuffle(seed=42)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T10:57:31.618851Z","iopub.execute_input":"2026-01-19T10:57:31.619077Z","iopub.status.idle":"2026-01-19T10:57:31.741588Z","shell.execute_reply.started":"2026-01-19T10:57:31.619043Z","shell.execute_reply":"2026-01-19T10:57:31.740992Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#==========================================\n# 3. 核心训练配置\n# ==========================================\nmodel_id = \"/kaggle/input/byt5-akkadian\" \ntokenizer = AutoTokenizer.from_pretrained(model_id)\nmodel = AutoModelForSeq2SeqLM.from_pretrained(model_id)\n\n# 特殊 Token 处理\nspecial_tokens = [\"<gap>\", \"<big_gap>\"]\nnum_added_toks = tokenizer.add_tokens(special_tokens)\nif num_added_toks > 0:\n    model.resize_token_embeddings(len(tokenizer))\n\n# Tokenization 优化：去掉 padding=\"max_length\"，交给 Collator\n# 1. 调整长度：256 是 ByT5 的高效平衡点\ndef tokenize_fn(ex):\n    model_inputs = tokenizer(ex[\"input_text\"], max_length=512, truncation=True)\n    labels = tokenizer(text_target=ex[\"target_text\"], max_length=512, truncation=True)\n    model_inputs[\"labels\"] = labels[\"input_ids\"]\n    return model_inputs\n\nfull_dataset = load_and_tag_data(\n        '/kaggle/input/deep-past-initiative-machine-translation/train.csv',\n    '/kaggle/input/new-dataset1-and-2-0118/gemini.xlsx'\n)\ntokenized_ds = full_dataset.train_test_split(test_size=0.1).map(tokenize_fn, batched=True, remove_columns=[\"input_text\", \"target_text\"])\n\ntraining_args = Seq2SeqTrainingArguments(\n    output_dir=\"./v7_checkpoints\",\n    \n    eval_strategy=\"epoch\",\n    save_strategy=\"epoch\",\n    load_best_model_at_end=True,\n    \n    # --- 核心防 OOM 修改 ---\n    per_device_train_batch_size=2,     # 降到最小，单次只读一条数据\n    gradient_accumulation_steps=16,    # 相应翻倍，保证总 batch_size 仍为 32\n    #gradient_checkpointing=True,      # 必须开启！用计算时间换取大量显存空间\n    \n    # --- 评估环节防 OOM ---\n    per_device_eval_batch_size=1,      # 验证时也只读一条，防止评估时 OOM\n    predict_with_generate=True,       \n    generation_max_length=40,          \n    \n    # --- 其他优化 ---\n    optim=\"adafactor\",                 \n    fp16=True,                         \n    num_train_epochs=20,               \n    learning_rate=5e-5,\n    logging_steps=20,\n    save_total_limit=1,\n    metric_for_best_model=\"sacrebleu\",\n    report_to=\"none\"\n)\n\n# 指标计算\nmetric = evaluate.load(\"sacrebleu\")\ndef compute_metrics(eval_preds):\n    preds, labels = eval_preds\n    if isinstance(preds, tuple): preds = preds[0]\n    # 将 -100 替换回 pad_token_id 以便解码\n    labels = np.where(labels != -100, labels, tokenizer.pad_token_id)\n    decoded_preds = tokenizer.batch_decode(preds, skip_special_tokens=True)\n    decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)\n    \n    # 过滤空值\n    decoded_preds = [p.strip() if p.strip() else \"empty\" for p in decoded_preds]\n    decoded_labels = [[l.strip()] for l in decoded_labels]\n    \n    result = metric.compute(predictions=decoded_preds, references=decoded_labels)\n    return {\"sacrebleu\": result[\"score\"]}\n\n# 初始化 Trainer\ntrainer = Seq2SeqTrainer(\n    model=model,\n    args=training_args,\n    train_dataset=tokenized_ds[\"train\"],\n    eval_dataset=tokenized_ds[\"test\"],\n    tokenizer=tokenizer,\n    data_collator=DataCollatorForSeq2Seq(tokenizer, model=model),\n    compute_metrics=compute_metrics\n)\n\n# 开始训练\ntrainer.train()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-19T10:57:31.742474Z","iopub.execute_input":"2026-01-19T10:57:31.742663Z","execution_failed":"2026-01-19T11:00:05.274Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ==========================================\n# 4. 终极模型保存逻辑\n# ==========================================\n# 训练完成后，trainer.model 已经是 load_best_model_at_end 后的最优权重\n\nfinal_save_path = \"./byt5_akkadian_model_with_dict\"\n\nprint(f\"--- Saving Best Model to {final_save_path} ---\")\ntrainer.save_model(final_save_path) # 这会保存 model, tokenizer 和 training_args\ntokenizer.save_pretrained(final_save_path)\n\n# 压缩为 zip 方便下载\nshutil.make_archive('byt5_akkadian_model_with_dict', 'zip', final_save_path)\nprint(\"Done! Model zipped: akkadian_model_release.zip\")","metadata":{"trusted":true,"execution":{"execution_failed":"2026-01-19T11:00:05.274Z"}},"outputs":[],"execution_count":null}]}