{"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":[{"sourceType":"competition","sourceId":121150,"databundleVersionId":15833391},{"sourceType":"datasetVersion","sourceId":14885274,"datasetId":9523472,"databundleVersionId":15748682},{"sourceType":"modelInstanceVersion","sourceId":755540,"databundleVersionId":15748859,"modelInstanceId":576974,"modelId":589299}],"dockerImageVersionId":31260,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"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\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-02-19T19:04:36.286518Z","iopub.execute_input":"2026-02-19T19:04:36.287316Z","iopub.status.idle":"2026-02-19T19:04:36.302454Z","shell.execute_reply.started":"2026-02-19T19:04:36.287272Z","shell.execute_reply":"2026-02-19T19:04:36.301573Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport os, sys, json, math, random\nimport numpy as np\nimport pandas as pd\nimport torch\n\nprint(\"python:\", sys.version)\nprint(\"torch:\", torch.__version__)\nprint(\"cuda available:\", torch.cuda.is_available())\nif torch.cuda.is_available():\n    print(\"gpu:\", torch.cuda.get_device_name(0))\nprint(\"gpu count:\", torch.cuda.device_count())\nfor i in range(torch.cuda.device_count()):\n    print(i, torch.cuda.get_device_name(i))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:04:36.304703Z","iopub.execute_input":"2026-02-19T19:04:36.304961Z","iopub.status.idle":"2026-02-19T19:04:36.312101Z","shell.execute_reply.started":"2026-02-19T19:04:36.304931Z","shell.execute_reply":"2026-02-19T19:04:36.311214Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"INPUT_DIR = \"/kaggle/input/competitions/deep-past-initiative-machine-translation\"\n\nfor root, _, files in os.walk(INPUT_DIR):\n    for f in files:\n        print(os.path.join(root, f))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:04:36.312960Z","iopub.execute_input":"2026-02-19T19:04:36.313208Z","iopub.status.idle":"2026-02-19T19:04:36.332591Z","shell.execute_reply.started":"2026-02-19T19:04:36.313187Z","shell.execute_reply":"2026-02-19T19:04:36.331875Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_path = f\"{INPUT_DIR}/train.csv\"\ntest_path  = f\"{INPUT_DIR}/test.csv\"\n\ntrain_df = pd.read_csv(train_path)\ntest_df  = pd.read_csv(test_path)\n\nprint(\"train shape:\", train_df.shape)\nprint(\"test  shape:\", test_df.shape)\n\nprint(\"\\ntrain columns:\", list(train_df.columns))\nprint(\"test  columns:\", list(test_df.columns))\n\ntrain_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:04:36.334405Z","iopub.execute_input":"2026-02-19T19:04:36.334661Z","iopub.status.idle":"2026-02-19T19:04:36.389248Z","shell.execute_reply.started":"2026-02-19T19:04:36.334628Z","shell.execute_reply":"2026-02-19T19:04:36.388530Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"SRC_COL_CANDIDATES = [\"transliteration\", \"source\", \"src\", \"akkadian\"]\nTGT_COL_CANDIDATES = [\"translation\", \"target\", \"tgt\", \"english\"]\n\ndef pick_col(df, candidates):\n    for c in candidates:\n        if c in df.columns:\n            return c\n    return None\n\nSRC_COL = pick_col(train_df, SRC_COL_CANDIDATES)\nTGT_COL = pick_col(train_df, TGT_COL_CANDIDATES)\n\nassert SRC_COL is not None, f\"Can't find source column in train.csv, tried {SRC_COL_CANDIDATES}\"\nassert TGT_COL is not None, f\"Can't find target column in train.csv, tried {TGT_COL_CANDIDATES}\"\n\nprint(\"Using SRC_COL =\", SRC_COL)\nprint(\"Using TGT_COL =\", TGT_COL)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:04:36.390171Z","iopub.execute_input":"2026-02-19T19:04:36.390446Z","iopub.status.idle":"2026-02-19T19:04:36.396274Z","shell.execute_reply.started":"2026-02-19T19:04:36.390417Z","shell.execute_reply":"2026-02-19T19:04:36.395451Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import re\n\ndef normalize_text(s: str) -> str:\n    if pd.isna(s):\n        return \"\"\n    s = str(s)\n    s = s.replace(\"\\u200b\", \" \").strip()  # 零宽字符\n    s = re.sub(r\"\\s+\", \" \", s)\n    return s\n\ntrain_df[SRC_COL] = train_df[SRC_COL].map(normalize_text)\ntrain_df[TGT_COL] = train_df[TGT_COL].map(normalize_text)\ntest_df[\"transliteration\"] = test_df[\"transliteration\"].map(normalize_text) if \"transliteration\" in test_df.columns else test_df.iloc[:, 1].map(normalize_text)\n\ntrain_df[[SRC_COL, TGT_COL]].head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:04:36.397300Z","iopub.execute_input":"2026-02-19T19:04:36.397610Z","iopub.status.idle":"2026-02-19T19:04:36.521031Z","shell.execute_reply.started":"2026-02-19T19:04:36.397577Z","shell.execute_reply":"2026-02-19T19:04:36.520277Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\nDOC_COL_CANDIDATES = [\"doc_id\", \"document_id\", \"text_id\", \"tablet_id\"]\nDOC_COL = next((c for c in DOC_COL_CANDIDATES if c in train_df.columns), None)\n#print(\"DOC_COL:\", DOC_COL)\n\nVAL_RATIO = 0.05\nSEED = 42\n\nif DOC_COL is None:\n    tr_df, va_df = train_test_split(train_df, test_size=VAL_RATIO, random_state=SEED)\nelse:\n    docs = train_df[DOC_COL].dropna().unique()\n    tr_docs, va_docs = train_test_split(docs, test_size=VAL_RATIO, random_state=SEED)\n    tr_df = train_df[train_df[DOC_COL].isin(tr_docs)].copy()\n    va_df = train_df[train_df[DOC_COL].isin(va_docs)].copy()\n\nprint(\"train split:\", tr_df.shape, \"valid split:\", va_df.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:04:36.521971Z","iopub.execute_input":"2026-02-19T19:04:36.522358Z","iopub.status.idle":"2026-02-19T19:04:36.530848Z","shell.execute_reply.started":"2026-02-19T19:04:36.522325Z","shell.execute_reply":"2026-02-19T19:04:36.529967Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from transformers import AutoTokenizer, AutoModelForSeq2SeqLM\n\nMODEL_NAME = \"/kaggle/input/models/jiayuanshe/byt5-small/transformers/default/1\"   # 也可以试 google/byt5-base（更慢更吃显存）\n\ntokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)\nmodel = AutoModelForSeq2SeqLM.from_pretrained(MODEL_NAME)\n\nprint(\"Loaded:\", MODEL_NAME)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:04:36.531870Z","iopub.execute_input":"2026-02-19T19:04:36.532342Z","iopub.status.idle":"2026-02-19T19:04:38.064183Z","shell.execute_reply.started":"2026-02-19T19:04:36.532310Z","shell.execute_reply":"2026-02-19T19:04:38.063309Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from datasets import Dataset\n\n# 可加一个任务前缀（对 T5 系列常有帮助；ByT5也可试）\nTASK_PREFIX = \"You are a Akkadian expert. Translate Akkadian latin-transliteration to English: \"\n\ndef to_dataset(df):\n    return Dataset.from_dict({\n        \"src\": (TASK_PREFIX + df[SRC_COL]).tolist(),\n        \"tgt\": df[TGT_COL].tolist()\n    })\n\ntrain_ds = to_dataset(tr_df)\nvalid_ds = to_dataset(va_df)\n\nMAX_SRC_LEN = 512\nMAX_TGT_LEN = 256\n\ndef preprocess(batch):\n    model_inputs = tokenizer(\n        batch[\"src\"],\n        max_length=MAX_SRC_LEN,\n        truncation=True\n    )\n    with tokenizer.as_target_tokenizer():\n        labels = tokenizer(\n            batch[\"tgt\"],\n            max_length=MAX_TGT_LEN,\n            truncation=True\n        )\n    model_inputs[\"labels\"] = labels[\"input_ids\"]\n    return model_inputs\n\ntrain_tok = train_ds.map(preprocess, batched=True, remove_columns=train_ds.column_names)\nvalid_tok = valid_ds.map(preprocess, batched=True, remove_columns=valid_ds.column_names)\n\n#train_tok[0]\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:54:20.206562Z","iopub.execute_input":"2026-02-19T19:54:20.206875Z","iopub.status.idle":"2026-02-19T19:54:21.916307Z","shell.execute_reply.started":"2026-02-19T19:54:20.206847Z","shell.execute_reply":"2026-02-19T19:54:21.915435Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"===== RAW DATA =====\")\nprint(\"train_df:\", len(tr_df))\nprint(\"valid_df:\", len(va_df))\nprint(\"test_df :\", len(test_df))\n\nprint(\"\\n===== TOKENIZED DATA =====\")\nprint(\"train_tok:\", len(train_tok))\nprint(\"valid_tok:\", len(valid_tok))\n\n# 检查是否有空样本\ndef count_empty(df, col):\n    return (df[col].astype(str).str.strip() == \"\").sum()\n\nprint(\"\\n===== EMPTY TEXT CHECK =====\")\nprint(\"empty train src:\", count_empty(train_df, SRC_COL))\nprint(\"empty train tgt:\", count_empty(train_df, TGT_COL))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:04:39.830437Z","iopub.execute_input":"2026-02-19T19:04:39.830671Z","iopub.status.idle":"2026-02-19T19:04:39.839576Z","shell.execute_reply.started":"2026-02-19T19:04:39.830649Z","shell.execute_reply":"2026-02-19T19:04:39.838643Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_src_len = train_df[SRC_COL].astype(str).apply(len)\ntrain_tgt_len = train_df[TGT_COL].astype(str).apply(len)\n\nprint(\"Source length: mean =\", train_src_len.mean(), \"max =\", train_src_len.max())\nprint(\"Target length: mean =\", train_tgt_len.mean(), \"max =\", train_tgt_len.max())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:04:39.840609Z","iopub.execute_input":"2026-02-19T19:04:39.840862Z","iopub.status.idle":"2026-02-19T19:04:39.859074Z","shell.execute_reply.started":"2026-02-19T19:04:39.840836Z","shell.execute_reply":"2026-02-19T19:04:39.858414Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"! pip install /kaggle/input/datasets/jiayuanshe/package/evaluate-0.4.6-py3-none-any.whl\n! pip install /kaggle/input/datasets/jiayuanshe/package/portalocker-3.2.0-py3-none-any.whl\n! pip install /kaggle/input/datasets/jiayuanshe/package/sacrebleu-2.6.0-py3-none-any.whl\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:04:39.859899Z","iopub.execute_input":"2026-02-19T19:04:39.861155Z","iopub.status.idle":"2026-02-19T19:04:50.230076Z","shell.execute_reply.started":"2026-02-19T19:04:39.861130Z","shell.execute_reply":"2026-02-19T19:04:50.229145Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import sacrebleu\nimport math\nimport numpy as np\n\nbleu_metric = sacrebleu.BLEU()\nchrfpp_metric = sacrebleu.CHRF(word_order=2)   # chrF++ !\n\ndef compute_metrics(eval_pred):\n    preds, labels = eval_pred\n    if isinstance(preds, tuple):\n        preds = preds[0]\n\n    pred_str = tokenizer.batch_decode(preds, skip_special_tokens=True)\n\n    labels = np.where(labels != -100, labels, tokenizer.pad_token_id)\n    label_str = tokenizer.batch_decode(labels, skip_special_tokens=True)\n\n    # sacrebleu expects list[str] + list[list[str]]\n    bleu = bleu_metric.corpus_score(pred_str, [label_str]).score\n    chrfpp = chrfpp_metric.corpus_score(pred_str, [label_str]).score\n\n    combo = math.sqrt(max(bleu, 0.0) * max(chrfpp, 0.0))\n\n    return {\n        \"bleu\": bleu,\n        \"chrfpp\": chrfpp,\n        \"dpi_score\": combo\n    }\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:04:50.231717Z","iopub.execute_input":"2026-02-19T19:04:50.232061Z","iopub.status.idle":"2026-02-19T19:04:50.241089Z","shell.execute_reply.started":"2026-02-19T19:04:50.232021Z","shell.execute_reply":"2026-02-19T19:04:50.240335Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from transformers import DataCollatorForSeq2Seq, Seq2SeqTrainer, Seq2SeqTrainingArguments\n\ndata_collator = DataCollatorForSeq2Seq(tokenizer=tokenizer, model=model)\n\nOUT_DIR = \"byt5_akkadian_en\"\n\nargs = Seq2SeqTrainingArguments(\n    output_dir=OUT_DIR,\n\n    # NEW API names\n    eval_strategy=\"steps\",\n    save_strategy=\"steps\",\n    logging_strategy=\"steps\",\n\n    eval_steps=500,\n    save_steps=500,\n    logging_steps=50,\n    save_total_limit=2,\n\n    per_device_train_batch_size=2,\n    per_device_eval_batch_size=2,\n    gradient_accumulation_steps=16,\n\n    learning_rate=2e-4,\n    num_train_epochs=6,\n    warmup_ratio=0.05,\n    weight_decay=0.01,\n\n    predict_with_generate=True,\n    generation_max_length=MAX_TGT_LEN,\n\n    fp16=torch.cuda.is_available(),\n    report_to=\"none\",\n    seed=SEED,\n)\ntrainer = Seq2SeqTrainer(\n    model=model,\n    args=args,\n    train_dataset=train_tok,\n    eval_dataset=valid_tok,\n    data_collator=data_collator,\n    compute_metrics=compute_metrics,\n    tokenizer=tokenizer,\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:04:50.241927Z","iopub.execute_input":"2026-02-19T19:04:50.242122Z","iopub.status.idle":"2026-02-19T19:04:50.616837Z","shell.execute_reply.started":"2026-02-19T19:04:50.242104Z","shell.execute_reply":"2026-02-19T19:04:50.616047Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"trainer.train()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:04:50.617958Z","iopub.execute_input":"2026-02-19T19:04:50.618497Z","iopub.status.idle":"2026-02-19T19:38:32.490739Z","shell.execute_reply.started":"2026-02-19T19:04:50.618473Z","shell.execute_reply":"2026-02-19T19:38:32.489925Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import pandas as pd\nimport matplotlib.pyplot as plt\n\nlogs = trainer.state.log_history\ndf_logs = pd.DataFrame(logs)\n\n# 训练 loss 通常在 \"loss\"，验证 loss 在 \"eval_loss\"\ntrain_loss = df_logs[df_logs[\"loss\"].notna()][[\"step\", \"loss\"]].copy() if \"loss\" in df_logs.columns else pd.DataFrame()\neval_loss  = df_logs[df_logs[\"eval_loss\"].notna()][[\"step\", \"eval_loss\"]].copy() if \"eval_loss\" in df_logs.columns else pd.DataFrame()\n\nprint(\"train loss points:\", len(train_loss))\nprint(\"eval loss points :\", len(eval_loss))\n\n# 画训练loss\nif len(train_loss) > 0:\n    plt.figure()\n    plt.plot(train_loss[\"step\"], train_loss[\"loss\"])\n    plt.xlabel(\"step\")\n    plt.ylabel(\"train loss\")\n    plt.title(\"Training Loss\")\n    plt.show()\n\n# 画验证loss\nif len(eval_loss) > 0:\n    plt.figure()\n    plt.plot(eval_loss[\"step\"], eval_loss[\"eval_loss\"])\n    plt.xlabel(\"step\")\n    plt.ylabel(\"eval loss\")\n    plt.title(\"Validation Loss\")\n    plt.show()\n\ndf_logs.tail(10)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:38:32.492106Z","iopub.execute_input":"2026-02-19T19:38:32.492489Z","iopub.status.idle":"2026-02-19T19:38:32.665943Z","shell.execute_reply.started":"2026-02-19T19:38:32.492447Z","shell.execute_reply":"2026-02-19T19:38:32.665164Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"metrics = trainer.evaluate()\nmetrics","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:38:32.667099Z","iopub.execute_input":"2026-02-19T19:38:32.667455Z","iopub.status.idle":"2026-02-19T19:40:06.408955Z","shell.execute_reply.started":"2026-02-19T19:38:32.667418Z","shell.execute_reply":"2026-02-19T19:40:06.408226Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from tqdm.auto import tqdm\n\nTEST_SRC_COL = \"transliteration\" if \"transliteration\" in test_df.columns else test_df.columns[1]\n\ntest_src = (TASK_PREFIX + test_df[TEST_SRC_COL].astype(str)).tolist()\n\nmodel.eval()\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel.to(device)\n\nBATCH = 16\nall_preds = []\n\nfor i in tqdm(range(0, len(test_src), BATCH)):\n    batch_text = test_src[i:i+BATCH]\n    enc = tokenizer(batch_text, return_tensors=\"pt\", padding=True, truncation=True, max_length=MAX_SRC_LEN).to(device)\n    with torch.no_grad():\n        gen = model.generate(\n            **enc,\n            max_new_tokens=MAX_TGT_LEN,\n            #max_new_tokens = 512,\n            num_beams=4\n        )\n    out = tokenizer.batch_decode(gen, skip_special_tokens=True)\n    all_preds.extend(out)\n\nprint(\"generated:\", len(all_preds))\nprint(all_preds[:3])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:54:29.024794Z","iopub.execute_input":"2026-02-19T19:54:29.025325Z","iopub.status.idle":"2026-02-19T19:54:32.065542Z","shell.execute_reply.started":"2026-02-19T19:54:29.025287Z","shell.execute_reply":"2026-02-19T19:54:32.064304Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\nplt.figure()\n\nif len(train_loss) > 0:\n    plt.plot(train_loss[\"step\"], train_loss[\"loss\"], label=\"train loss\")\n\nif len(eval_loss) > 0:\n    plt.plot(eval_loss[\"step\"], eval_loss[\"eval_loss\"], label=\"eval loss\")\n\nplt.xlabel(\"step\")\nplt.ylabel(\"loss\")\nplt.title(\"Train vs Eval Loss\")\nplt.legend()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:51:42.320973Z","iopub.execute_input":"2026-02-19T19:51:42.321601Z","iopub.status.idle":"2026-02-19T19:51:42.455016Z","shell.execute_reply.started":"2026-02-19T19:51:42.321571Z","shell.execute_reply":"2026-02-19T19:51:42.454178Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sub = pd.DataFrame({\n    \"id\": test_df[\"id\"].values,\n    \"translation\": all_preds\n})\n\nsub.to_csv(\"submission.csv\", index=False)\nsub.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:51:45.332074Z","iopub.execute_input":"2026-02-19T19:51:45.332847Z","iopub.status.idle":"2026-02-19T19:51:45.342754Z","shell.execute_reply.started":"2026-02-19T19:51:45.332814Z","shell.execute_reply":"2026-02-19T19:51:45.341909Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# evaluate\nimport math\n\nbleu_metric = sacrebleu.BLEU()                 # corpus BLEU\nchrfpp_metric = sacrebleu.CHRF(word_order=2)   # chrF++: word_order=2 :contentReference[oaicite:2]{index=2}\n\ndef dpi_official_score(preds, refs):\n    \"\"\"\n    preds: list[str]  模型生成\n    refs : list[str]  参考答案（同长度）\n    返回: dict\n    \"\"\"\n    # sacrebleu 需要 references 是 list[list[str]] 结构（多参考可扩展）\n    bleu = bleu_metric.corpus_score(preds, [refs]).score\n    chrfpp = chrfpp_metric.corpus_score(preds, [refs]).score\n    combo = math.sqrt(max(bleu, 0.0) * max(chrfpp, 0.0))  # Score = sqrt(BLEU * chrF++) :contentReference[oaicite:3]{index=3}\n    return {\"bleu\": bleu, \"chrfpp\": chrfpp, \"score\": combo}\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:54:39.284171Z","iopub.execute_input":"2026-02-19T19:54:39.284950Z","iopub.status.idle":"2026-02-19T19:54:39.291324Z","shell.execute_reply.started":"2026-02-19T19:54:39.284911Z","shell.execute_reply":"2026-02-19T19:54:39.290446Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 让 trainer 用 generate 在 valid_tok 上预测\npred_out = trainer.predict(valid_tok, max_length=MAX_TGT_LEN)\n\npred_ids = pred_out.predictions\nlabel_ids = pred_out.label_ids\n\n# 有些版本 predictions 是 tuple\nif isinstance(pred_ids, tuple):\n    pred_ids = pred_ids[0]\n\npred_texts = tokenizer.batch_decode(pred_ids, skip_special_tokens=True)\n\n# label 里 -100 需要替换成 pad_token_id 才能 decode\nlabel_ids = np.where(label_ids != -100, label_ids, tokenizer.pad_token_id)\nref_texts = tokenizer.batch_decode(label_ids, skip_special_tokens=True)\n\nscores = dpi_official_score(pred_texts, ref_texts)\nscores\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T19:54:43.461177Z","iopub.execute_input":"2026-02-19T19:54:43.461530Z","iopub.status.idle":"2026-02-19T19:55:47.354126Z","shell.execute_reply.started":"2026-02-19T19:54:43.461501Z","shell.execute_reply":"2026-02-19T19:55:47.353303Z"}},"outputs":[],"execution_count":null}]}