{
  "id": 684231,
  "title": "[6th] DPC solution - 15 models ensemble on diverse data sources",
  "url": "/competitions/deep-past-initiative-machine-translation/writeups/dpc-6th-solution",
  "author_name": "",
  "post_date": "2026-03-24T05:59:08.053Z",
  "votes": 54,
  "comment_count": 12,
  "views": 0,
  "content": "<p>First of all, I’d like to thank my teammate <a href=\"https://www.kaggle.com/jackvd\" target=\"_blank\">@jackvd</a> for providing high-quality data and clever data cleaning methods, as well as for constantly bringing fresh ideas to the table. I also want to thank <a href=\"https://www.kaggle.com/marisyn\" target=\"_blank\">@marisyn</a> for providing numerous excellent APIs and handling the PDF extraction work! And all personnel from the host organization, DPI.</p>\n<p>Our solution focuses on three key areas: data, training, and inference.</p>\n<h1>1. Data Extract</h1>\n<h2>PDF Data</h2>\n<p>The organizers provided a large number of <a href=\"https://www.kaggle.com/datasets/deeppast/old-assyrian-kltepe-tablets-in-pdf\" target=\"_blank\">PDFs</a> (There is also a <em>Larsen 2002</em> in <a href=\"https://www.kaggle.com/datasets/deeppast/old-assyrian-grammars-and-other-resources\" target=\"_blank\">here</a>.), so our first challenge was figuring out how to effectively extract text from them.</p>\n<p>After reviewing these PDFs, we have concluded that they can primarily be categorized into three types: image, text, and broken_text:</p>\n<ul>\n<li><p><strong>Image type</strong>: Although these have an OCR text layer, OCR artifacts become clearly visible when zooming in on the PDF. This category includes: AKT 1 1990, AKT 11a, AKT 2 1995, AKT 3 1995, AKT 4 2006, AKT 6a, AKT 6b, AKT 6c, AKT 6d, AKT 6e, and AKT_12.</p></li>\n<li><p><strong>Text type</strong>: Zooming in does not degrade the clarity of the PDF in any way. Furthermore, the text layer extracted using PyMuPDF has an exact one-to-one correspondence with what is visible to the naked eye. For example, using this code to handle one-to-one character error issues:</p></li>\n</ul>\n<pre><code># AKT 5 2008\n_MACRON = {\n    \"a\": \"ā\", \"e\": \"ē\", \"i\": \"ī\", \"u\": \"ū\",\n    \"A\": \"Ā\", \"E\": \"Ē\", \"I\": \"Ī\", \"U\": \"Ū\",\n}\n_MARKERS_CLASS = r\"[\\u00a3\\u2039\\u2122]+\"          # £‹™ (allow multi)\n_MARKER_VOWEL_RE = re.compile(_MARKERS_CLASS + r\"([aeiuAEIU])\")\n\ndef apply_akt5_vowel_marker_rule(text: str) -&gt; str:\n    text = _MARKER_VOWEL_RE.sub(lambda m: _MACRON[m.group(1)], text)\n    text = re.sub(_MARKERS_CLASS, \"\", text)\n    return text\n</code></pre>\n<p>This category includes: AKT 10, AKT 11b, AKT 5 2008, AKT 7b, and AKT 9a.</p>\n<ul>\n<li><strong>Broken_text type</strong>: Similar to the text type, zooming in does not affect clarity. However, the text layer extracted using PyMuPDF exhibits a one-to-many or many-to-many correspondence with what is visually observed. </li>\n</ul>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18505507%2F05787bbe3e387d112d2845c6df35a3f0%2F4.png?generation=1774319930773772&amp;alt=media\" alt=\"example AKT 8 2015\"></p>\n<p>This category includes: AKT 8 2015 and AKT 7a.</p>\n<p>For the broken_text type, we will directly treat them as the image type.</p>\n<p>Once the file type is identified, we pass text-based PDFs directly to the LLM API for processing, where it extracts the corresponding document pairs (which are later split into sentences). The method involves using Regular Expressions (Regex) to identify heading blocks, then passing both the headings and their corresponding text blocks to a LLM api for analysis and the extraction of document pairs (including error correction).</p>\n<p>For image-based PDFs, our workflow is as follows: \nwe first utilize the <a href=\"https://www.kaggle.com/code/angantyr/text-extraction-from-pdf-docs-using-glm-oc\" target=\"_blank\">open-source GLM-OCR method</a>, featuring custom parameter tuning and parallelization enhancements. Then we can get this:</p>\n<pre><code>{\n    \"p-1_f-0\": \"1. kt 94/k 1263\",\n    \"p-1_f-1\": \"14 1/2 GÍN KB \"KI\" I-lí-dan\",\n    \"p-1_f-2\": \"14 1/2 shekels of silver is owed by Ili-dan\",\n    \"p-1_f-3\": \"DUMU Šu-ki-tim 2 ma-na 10 GÍN / KU\",\n    \"p-1_f-4\": \"son of Sukkutum. 2 minas 10 shekels of\",\n    ...\n}\n</code></pre>\n<table>\n<thead>\n<tr>\n<th></th>\n<th>page_num</th>\n<th>field_num</th>\n<th>x</th>\n<th>y</th>\n<th>w</th>\n<th>h</th>\n<th>x_pad</th>\n<th>y_pad</th>\n<th>w_pad</th>\n<th>h_pad</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n<td>-1</td>\n<td>0</td>\n<td>0</td>\n<td>2075</td>\n<td>2809</td>\n<td>0</td>\n<td>0</td>\n<td>2075</td>\n<td>2809</td>\n</tr>\n<tr>\n<td>1</td>\n<td>1</td>\n<td>0</td>\n<td>864</td>\n<td>253</td>\n<td>378</td>\n<td>50</td>\n<td>852</td>\n<td>241</td>\n<td>402</td>\n<td>74</td>\n</tr>\n<tr>\n<td>2</td>\n<td>1</td>\n<td>1</td>\n<td>166</td>\n<td>403</td>\n<td>533</td>\n<td>48</td>\n<td>154</td>\n<td>391</td>\n<td>557</td>\n<td>72</td>\n</tr>\n<tr>\n<td>3</td>\n<td>1</td>\n<td>2</td>\n<td>1167</td>\n<td>410</td>\n<td>746</td>\n<td>52</td>\n<td>1155</td>\n<td>398</td>\n<td>770</td>\n<td>76</td>\n</tr>\n<tr>\n<td>4</td>\n<td>1</td>\n<td>3</td>\n<td>162</td>\n<td>470</td>\n<td>773</td>\n<td>49</td>\n<td>150</td>\n<td>458</td>\n<td>797</td>\n<td>73</td>\n</tr>\n<tr>\n<td>…</td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n</tbody>\n</table>\n<p><br></p>\n<p>Next, we use Regex to match the headings again. The LLM API then processes these headings and images to generate text. Finally, we run a second pass with the image and text together for fine-grained correction, ensuring a high degree of accuracy.</p>\n<p>Some of these PDFs are in Turkish. We're using a combination of LLM APIs with dictionaries &amp; Turkish translation to translate them into English for training.</p>\n<hr>\n<h2>Train.csv</h2>\n<p>Regarding <code>train.csv</code>, I have to mention the <strong>OARE</strong> website. For each sample, we visit <code>https://oare.byu.edu/epigraphies/{oare_id}</code>. </p>\n<p>We discovered that clicking on a specific word on this page displays its definition. Consequently, we <strong>scraped</strong> the site to obtain the meanings of these words. While not every word has a corresponding definition, the vast majority do, which is more than sufficient for our needs.</p>\n<p>As a result, we obtained a dictionary covering most of the words in train.csv. </p>\n<p>However, it is important to note that almost all documents in train.csv originate from AKT 5 2008, AKT 6a, AKT 6b, AKT 6c, AKT 6d, AKT 6e, and AKT 8 2015. To improve accuracy, we performed similarity matching between the entries in train.csv and the text extracted from these PDF sources. This allowed us to retrieve the \"ground truth\" or correct form of the documents, as the data quality within the original PDFs is significantly higher. (This involved a lot of manual fixes.)</p>\n<p>Following these procedures, we used that dictionary to give the LLM a general understanding of what transliteration means, then followed the English sentence breaks to eventually obtain sentence-level parallel corpora. (The alignment in this step might not be perfect, so we have implemented corresponding AUG to address this.)</p>\n<hr>\n<h2>Published_texts.csv</h2>\n<p>Similar to train.csv, almost all samples can be found on the OARE website. However, since translations were unavailable, we used an LLM API to create pseudo-labels for these samples. The process is roughly as follows: we prompt the LLM to translate the original text using the dictionary we retrieved from the OARE site.</p>\n<p>example (The prompt also includes several constraints, such as JSON formatting, mana 60 conversion, …):</p>\n<pre><code>key_prompt = \"\"\"\nKIŠIB a-šùr-ma-lik DUMU i-na-a a-na a-šùr-na-da DUMU &lt;gap&gt; ù en-um-a-šùr DUMU &lt;gap&gt; a-pu-tum a-na a-wa-at ṭup-pì-im iḫ-da &lt;gap&gt; ša ú-&lt;gap&gt; ḫa-sí-sú &lt;gap&gt;\n\n{KIŠIB|form:Noun|1. seal, cylinder seal 2. seal impression produced by a cylinder seal 3. sealed clay tablet (legal or administrative document, also letter)}\n{a-šùr-ma-lik|form:Noun|1. Aššur is counselor}\n{DUMU|form:Noun|1. son}\n{i-na-a|form:Noun|nan}\n{a-na|form:Preposition|1. to, for, up to,  toward, against, upon}\n{a-šùr-na-da|form:Noun|1. Praise Aššur!}\n{ù|form:Conjunction|1. and}\n{en-um-a-šùr|form:Noun|1. Mercy O Aššur}\n{a-pu-tum|form:Interjection|1. urgent!, please!}\n{a-wa-at|form:Noun|1. word, matter, lawsuit 2. pl. awâtum - stipulation, rule}\n{ṭup-pì-im|form:Noun|1. (inscribed) tablet (of clay, rarely of other materials) 2. board, flat surface}\n{iḫ-da|form:Finite verb|1. to attend, to watch, to pay attention, to do something carefully 2. to draw someone's attention to something, to alert, to ask someone to pay attention, to take care}\n{ša|form:Pronoun|1. relative pronoun}\n\"\"\"\n</code></pre>\n<hr>\n<h2>Publications.csv</h2>\n<p>For this file, we provide a fixed number of pages to the LLM API to extract sentences and other elements. While this yields a significant amount of data, it is difficult to achieve document-level alignment.</p>\n<hr>\n<p>With that, we have obtained a parallel corpus dataset; the remaining task is now focused on data cleaning.</p>\n<h1>2. Data Processing</h1>\n<p>Actually, the organizers updated the data several times. The <code>train.csv</code> I used in Step 1 to match against the PDF data was the second version, which was much better normalized.</p>\n<p>For preprocessing, here’s the main stuff (mainly for pdf-data or LLM results, The second version of train.csv is already quite clean.):</p>\n<p><strong>General Foundation Processing (Applied to Both Sides)</strong></p>\n<ul>\n<li>Character-level normalization: Apply Unicode NFKC normalization, remove zero-width characters, and unify the formatting of dashes and hyphens.</li>\n<li>Special letter simplification: Convert special characters with diacritics (e.g., Ḫ, ḫ) to standard letters (e.g., H, h).</li>\n<li>Removal of academic editorial symbols: Eliminate various special editorial brackets commonly found in the literature (e.g., ⸢ ⸣ ⌈ ⌉ ⟦ ⟧ [ ], etc.) and footnote superscript numbers.</li>\n<li>Fraction and decimal formatting: Intelligently convert decimals, mixed numbers (e.g., 1 1/2), or regular fractions in the text into compact Unicode single-character fractions (e.g., ½, ⅓, ¾).</li>\n</ul>\n<hr>\n<p><strong>Transliteration Text Side</strong>\nThe primary objective of the source text side is to eliminate typographical variations within the sign system and to standardize determinatives for divine names, toponyms, and similar entities.</p>\n<ul>\n<li>Determinative and script normalization:<ul>\n<li>Convert subscript numbers (e.g., ₀₁₂) to standard digits.</li>\n<li>Unify superscript determinatives (e.g., ᵏⁱ, ᵈUTU) or superscripts indicated by carets (e.g., ^{ki}) into standard curly brace syntax (e.g., {ki}, {d}UTU).</li>\n<li>Intelligently identify and merge determinatives that are separated from proper nouns (e.g., d UTU -&gt; {d}UTU).</li></ul></li>\n<li>Sign composition and placeholder cleanup:<ul>\n<li>Convert multiplication signs (×, ✖) representing sign compounding into periods (.).</li>\n<li>Clean up or convert the unknown sign marker \"x\" attached to the end of a word or before a parenthesis (e.g., SUDx -&gt; SUD).</li></ul></li>\n<li>Removal of specific academic annotations: Remove specifically formatted parenthetical annotations (e.g., editorial notes like \"(over erased)\", \"(met.)\"), standalone line numbers (e.g., \"10.\"), and the pipe character (|) unique to the Oracc system.</li>\n<li>Gap handling and alignment: Unify and normalize various symbols indicating missing text (e.g., consecutive periods \"…\", the standalone letter \"x\", \"missing\", etc.) into a single placeholder <code>&lt;gap&gt;</code>, and strictly clean up extraneous spaces and punctuation before and after the <code>&lt;gap&gt;</code> marker.</li>\n<li>Dictionary-level hard replacement: Erase punctuation marks such as question marks and exclamation points based on the <code>transliteration_map</code>, and correct specific spellings of personal names and anomalous annotations.</li>\n</ul>\n<hr>\n<p><strong>Translation Side</strong>\nThe primary objective of the translation side is to enhance the natural linguistic fluency of the text, eliminating alternative options and academic jargon left by translators.</p>\n<ul>\n<li>Aggressive annotation and markup clearance: Compared to the source text side, the translation side indiscriminately removes all translational explanations within parentheses, while also eliminating asterisks (*) and quotation marks (\") used for emphasis.</li>\n<li>Academic/scribal terminology removal: Automatically identify and delete academic annotations such as \"fem. plur.\" (feminine plural), \"lit.\" (literally), \"sic\" (thus in the original), \"i.e.\", and similar terms.</li>\n<li>Intelligent decision-making for slash alternatives:<ul>\n<li>When an \"either/or\" scenario appears in the translation (e.g., a / the, like / instead, promised / been), the script automatically selects the vocabulary that better fits the context or predefined settings.</li>\n<li>Automatically handle capitalization inheritance (if the original word is capitalized, the selected word will automatically be capitalized).</li></ul></li>\n<li>Roman numeral month conversion: Identify expressions such as \"Month XII\" and convert them to Arabic numerals, such as \"Month 12\".</li>\n<li>Expansion of complex measurement units: Based on the <code>translation_map</code>, expand complex fractional weights unique to antiquity (e.g., 12 5/12 shekels) into more readable text (e.g., 12 ⅓ shekels 15 grains).</li>\n<li>Punctuation and typographical correction: Fix extraneous spaces before punctuation marks (e.g., commas, semicolons) and ensure compactness between numbers and fractions.</li>\n<li>Gap mapping: Similarly, replace \"x\" placeholders or ellipses appearing in the translation with <code>&lt;gap&gt;</code> to ensure strict alignment with the source text side in subsequent machine learning or text comparison processes.</li>\n</ul>\n<hr>\n<p>Actually, this part skips over a considerable amount of manual effort—things like manually checking for anomalies, using regex for matching, and then incorporating those into the cleaning rules, this is our final clean func:</p>\n<p><a href=\"https://www.kaggle.com/code/wscmisx/dpc-clean-function/notebook?scriptVersionId=306066643\" target=\"_blank\">clean function</a></p>\n<h1>3. Training</h1>\n<ul>\n<li><strong>Dynamic Context Sampling</strong></li>\n</ul>\n<p>This technique aims to address two primary issues:</p>\n<ol>\n<li>Potential sentence misalignments during the extraction phase; we intend to make the model less sensitive to sentence alignment.</li>\n<li>The unknown sentence length distribution of the test set, which may differ from our segmented data; we utilize this approach to enable the model to adapt to varying input and output lengths.</li>\n</ol>\n<p>Specifically, we implemented the following mechanisms:</p>\n<ul>\n<li><strong>Zipf / Geometric Mixture Distribution Sampling (K)</strong>: The number of contextual sentences included is dynamically determined for each instance . Following Zipf's law (long-tail distribution), the model predominantly encounters short contexts and occasionally processes extremely long contexts, thereby enhancing its adaptability to varying text lengths.</li>\n<li><strong>Symmetric / Asymmetric Allocation (Symmetric LR)</strong>: The ratio of left to right contextual sentences (L and R) is dynamically determined.</li>\n<li><strong>Random Contextual Sentence Insertion (Random Context)</strong>: There is a 20% probability (<code>random_ctx_prob = 0.20</code>) of bypassing contiguous context and instead randomly sampling and concatenating sentences from the same article. This compels the model to learn not only the local context but also the global topic, preventing the rote memorization of absolute sentence orders.</li>\n</ul>\n<pre><code>def _build_k_cdf(self):\n    \"\"\"\n    Build a CDF over K in [1, k_max].\n    - zipf:     P(K) ∝ 1/K  (heavy-tail, good length coverage)\n    - geom_mix: (1-α)·Geom(p) + α·Zipf(1/K)\n    \"\"\"\n    keys = list(range(1, self.k_max + 1))\n\n    if self.k_sampling == \"geom_mix\":\n        # geometric over {1..k_max}: p*(1-p)^(k-1)\n        geom, zipf = [self.geom_p * ((1.0 - self.geom_p) ** (k - 1)) for k in keys], [1.0 / k for k in keys]\n        probs = [(1.0 - self.long_tail_mix) * g + self.long_tail_mix * z for g, z in zip(geom, zipf)]\n    else:\n        # default: Zipf / 1-K\n        probs = [1.0 / k for k in keys]\n\n    s = sum(probs); probs = [p / s for p in probs]\n    cdf = list(accumulate(probs))\n    cdf[-1] = 1.0\n    return keys, cdf\n\ndef _sample_K(self):\n    r = random.random()\n    i = bisect_left(self._k_cdf, r)\n    if i &gt;= len(self._k_keys): i = len(self._k_keys) - 1\n    return int(self._k_keys[i])\n\n@staticmethod\ndef _sample_LR(K: int):\n    # L ~ Binomial(K-1, 0.5), R = K-1-L\n    if K &lt;= 1: return 0, 0\n    L = int(np.random.binomial(K - 1, 0.5))\n    R = int((K - 1) - L)\n    return L, R\n</code></pre>\n<ul>\n<li><strong>R-Drop</strong></li>\n</ul>\n<p>For a given input x, it is passed through the network twice (due to Dropout, the two forward propagation results, y_1 and y_2, will exhibit minor variances). In addition to calculating the standard cross-entropy loss, the symmetric Kullback-Leibler (KL) divergence between these two output distributions (logits) is computed. This enforces output consistency in the presence of internal noise (Dropout), significantly mitigating overfitting.</p>\n<ul>\n<li><strong>PGD - Projected Gradient Descent</strong></li>\n</ul>\n<p>This involves identifying the \"worst-case perturbations\" within the continuous embedding space and injecting them into the input, ensuring that the model can still produce accurate predictions even under slight adversarial perturbations.</p>\n<ul>\n<li><strong>EMA</strong></li>\n</ul>\n<p>Exponential Moving Average, a commonly adopted technique in deep learning competitions to improve model generalization and stability.</p>\n<ul>\n<li><strong>Engineering Optimizations</strong></li>\n</ul>\n<p>Incorporating these complex components necessitates engineering optimizations, which include:</p>\n<ul>\n<li>DeepSpeed ZeRO-2</li>\n<li>Gradient Checkpointing</li>\n<li>Custom Trainer implementation</li>\n</ul>\n<p>Final parameters:</p>\n<pre><code>k_max=10\nk_sampling=\"zipf\"\ngeom_p=0.35\nrandom_ctx_prob=0.20\nRDROP_ALPHA = 0.8\nPGD_ENABLE = True\nPGD_STEPS  = 3\nPGD_EPS    = 0.5\nPGD_ALPHA  = 0.2\nPGD_EMB_KEYWORDS = (\"shared\", \"embed_tokens\", \"embeddings\")\n</code></pre>\n<h1>4. Experiment</h1>\n<p>This is our primary experimental pipeline (the optimal path for single-mode optimization).\nBy using different subsets of data, we trained a variety of models. Generally, it works like this:</p>\n<table>\n<thead>\n<tr>\n<th>Model</th>\n<th>Method</th>\n<th>LB</th>\n<th>PB</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>byt5-large</td>\n<td>v1 No external data (only aligned and cleaned <code>train.csv</code>)</td>\n<td>34.0</td>\n<td>34.3</td>\n</tr>\n<tr>\n<td>byt5-large</td>\n<td>v2 Added Turkish-to-English data</td>\n<td>36.9</td>\n<td>37.0</td>\n</tr>\n<tr>\n<td>byt5-large</td>\n<td>v3 Added high quality publications data</td>\n<td>38.7</td>\n<td>38.9</td>\n</tr>\n<tr>\n<td>byt5-large</td>\n<td>v4 Pre-trained for 5 epochs using all <code>publications</code> data before formal training (filtered by quality)</td>\n<td>38.6</td>\n<td>39.5</td>\n</tr>\n<tr>\n<td>byt5-large</td>\n<td>v5 Pre-trained for 5 epochs using all <code>publications</code> data and <code>published_texts</code> pseudo-labels before formal training (filtered by quality)</td>\n<td>38.9</td>\n<td>39.3</td>\n</tr>\n<tr>\n<td>byt5-base</td>\n<td>v6 Formally trained using <code>publications</code> data, <code>published_texts</code> pseudo-labels, alongside main data</td>\n<td>39.3</td>\n<td>39.9</td>\n</tr>\n</tbody>\n</table>\n<p><br></p>\n<p>Initially, we thought the extra data was too noisy and left it out of the formal training phase, but it actually performed better once we included it—probably just because of the large volume.</p>\n<h1>5. Inference</h1>\n<p>Our final submission consists of 15 models, primarily detailed as follows:</p>\n<table>\n<thead>\n<tr>\n<th>Model Description</th>\n<th>LB</th>\n<th>PB</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Large model trained on data subset v3</td>\n<td>38.7</td>\n<td>38.9</td>\n</tr>\n<tr>\n<td>Large model trained on data subset v3 with name augmentation</td>\n<td>37.8</td>\n<td>38.8</td>\n</tr>\n<tr>\n<td>XL model trained on data subset v3</td>\n<td>37.9</td>\n<td>38.3</td>\n</tr>\n<tr>\n<td>Large model trained on data subset v4</td>\n<td>38.6</td>\n<td>39.5</td>\n</tr>\n<tr>\n<td>Large model trained on data subset v5</td>\n<td>38.9</td>\n<td>39.3</td>\n</tr>\n<tr>\n<td>Base model trained on data subset v5</td>\n<td>38.8</td>\n<td>39.0</td>\n</tr>\n<tr>\n<td>Base model trained on data subset v6</td>\n<td>39.2</td>\n<td>39.5</td>\n</tr>\n<tr>\n<td>Base model trained on data subset v6 with additional filtering and cleaning (v1)</td>\n<td>39.3</td>\n<td>39.9</td>\n</tr>\n<tr>\n<td>Base model trained on data subset v6 with additional filtering and cleaning (v2)</td>\n<td>38.3</td>\n<td>39.2</td>\n</tr>\n<tr>\n<td>Base model trained on data subset v6 with additional filtering and cleaning (v3)</td>\n<td>38.2</td>\n<td>39.5</td>\n</tr>\n<tr>\n<td>Base model trained on data subset v6 with additional filtering and cleaning (v4)</td>\n<td>38.4</td>\n<td>39.6</td>\n</tr>\n</tbody>\n</table>\n<p>We performed 5-fold cross-validation training based on our best-performing model on the Public Leaderboard (LB score 39.3: Base model trained on data subset v6 with additional filtering and cleaning (v1)). Although these models were not submitted individually, they were also incorporated into the MBR (Minimum Bayes Risk) ensemble.</p>\n<p>To accelerate inference, we restricted the output to four results per model using a beam size of 4 (<code>beam=4</code>), without applying any sampling methods (our experiments indicated that utilizing sampling within the MBR process leads to a decrease in score). </p>\n<p>The models were grouped and deployed across two T4 GPUs for execution.</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18505507%2F2d32d5e4df1b2bbafc1e969535c8f85f%2Fall_models.png?generation=1774331390404829&amp;alt=media\" alt=\"15models\"></p>\n<p><a href=\"https://www.kaggle.com/code/wscmisx/final-dpc-ensemble-mbr-infer\" target=\"_blank\"><strong>Final Score:</strong> 40.7  |  <strong>Execution Time:</strong> 208m 50s</a></p>\n<h1>6. What didn't work</h1>\n<ul>\n<li><strong>Organizing the current target sentence and its context like this (during both training and inference):</strong></li>\n</ul>\n<pre><code>translate Akkadian to English. Translate TARGET only. Use CONTEXT only as reference.\nTARGET:\nki-ma mup-pì-ni ta-áa-me-a-ni a-ma-kam lu a-na aí-mì-im a-na É.GAL-lim i-dí-in lu té-ra-at É.GAL-lim ú-kà-lim lu na-aí-ma a-dí-ni lá i-dí-in ma-lá KÙ.AN na-áa-ú ni-bi„-it a-aí-im au-um-au ú au-mì a-bi„-au i-na mup-pì-im lu-up-ta-nim-ma ia-tí aí-ip-ri-ni aé-bi„-lá-nim\n\nCONTEXT BEFORE:\num-ma kà-ru-um kà-ni-ia-ma a-na aa-qí-il… da-tim aí-ip-ri-ni kà-ar kà-ar-ma ú wa-bar-ra-tim qí-bi„-ma mup-pu-um aa a-lim(ki) i-li-kam i-na mup-pì-im aa a-lim(ki) ia-tù u„-mì-im a-nim ma-ma-an KÙ.AN i-aa-ú-mu-ni i-na né-mì-lim da-aùr ú-lá e-WA ia-ra-tí-au kà-ru-um kà-ni-ia i-lá-qé\n\nCONTEXT AFTER:\nme-+e-er mup-pì-ni a-na kà-ar kà-ar-ma ú wa-bar-ra-tim aé-bi„-lá KÙ.AN lu a-na DAM.GÀR-ru-tim i-dí-in au-mì a-wi-lim lu-up-ta-nim\n</code></pre>\n<ul>\n<li><p><strong>Data augmentation on names and dictionaries in the source text during training</strong>\nRandomly altering the capitalization of names, and using a dictionary to randomly select a <code>norm</code> form for many-to-one <code>norm</code> -&gt; <code>lexeme</code> mappings. \nThe single-model score didn't improve much, but we still included these in our ensemble.</p></li>\n<li><p><strong>Weight fusion / averaging</strong>\nThis showed almost no improvement and even resulted in lower scores than the original single model. After a few attempts, we stopped trying this and switched to using MBR (Minimum Bayes Risk) for ensembling instead.</p></li>\n<li><p><strong>Setting a very long <code>maxlen</code> and training at the document level</strong>\nUnsurprisingly, the results were poor because the test set is evaluated at the sentence level.</p></li>\n<li><p><strong>Using Large Language Models (Qwen, LLaMA, etc.)</strong>\nThis was likely due to the tokenizer or the fact that my data wasn't clean enough in the early stages of the competition. Without applying any special tricks, the performance of a trained 14B model was only average. However, I was very glad to see top-ranking participants successfully taking on the challenge using LLMs—an excellent solution!</p></li>\n<li><p><strong>Bidirectional training (adding <code>akk-&gt;eng</code>)</strong>\nThere wasn't much progress in terms of single-model improvement. We didn't have extra submission quotas to test this before ensembling, but perhaps including it in the ensemble could have provided a slight boost to the final results.</p></li>\n</ul>",
  "messages": [
    {
      "id": "3427476",
      "postDate": "03/24/2026 05:58:31",
      "content": "<p>First of all, I’d like to thank my teammate <a href=\"https://www.kaggle.com/jackvd\" target=\"_blank\">@jackvd</a> for providing high-quality data and clever data cleaning methods, as well as for constantly bringing fresh ideas to the table. I also want to thank <a href=\"https://www.kaggle.com/marisyn\" target=\"_blank\">@marisyn</a> for providing numerous excellent APIs and handling the PDF extraction work! And all personnel from the host organization, DPI.</p>\n<p>Our solution focuses on three key areas: data, training, and inference.</p>\n<h1>1. Data Extract</h1>\n<h2>PDF Data</h2>\n<p>The organizers provided a large number of <a href=\"https://www.kaggle.com/datasets/deeppast/old-assyrian-kltepe-tablets-in-pdf\" target=\"_blank\">PDFs</a> (There is also a <em>Larsen 2002</em> in <a href=\"https://www.kaggle.com/datasets/deeppast/old-assyrian-grammars-and-other-resources\" target=\"_blank\">here</a>.), so our first challenge was figuring out how to effectively extract text from them.</p>\n<p>After reviewing these PDFs, we have concluded that they can primarily be categorized into three types: image, text, and broken_text:</p>\n<ul>\n<li><p><strong>Image type</strong>: Although these have an OCR text layer, OCR artifacts become clearly visible when zooming in on the PDF. This category includes: AKT 1 1990, AKT 11a, AKT 2 1995, AKT 3 1995, AKT 4 2006, AKT 6a, AKT 6b, AKT 6c, AKT 6d, AKT 6e, and AKT_12.</p></li>\n<li><p><strong>Text type</strong>: Zooming in does not degrade the clarity of the PDF in any way. Furthermore, the text layer extracted using PyMuPDF has an exact one-to-one correspondence with what is visible to the naked eye. For example, using this code to handle one-to-one character error issues:</p></li>\n</ul>\n<pre><code># AKT 5 2008\n_MACRON = {\n    \"a\": \"ā\", \"e\": \"ē\", \"i\": \"ī\", \"u\": \"ū\",\n    \"A\": \"Ā\", \"E\": \"Ē\", \"I\": \"Ī\", \"U\": \"Ū\",\n}\n_MARKERS_CLASS = r\"[\\u00a3\\u2039\\u2122]+\"          # £‹™ (allow multi)\n_MARKER_VOWEL_RE = re.compile(_MARKERS_CLASS + r\"([aeiuAEIU])\")\n\ndef apply_akt5_vowel_marker_rule(text: str) -&gt; str:\n    text = _MARKER_VOWEL_RE.sub(lambda m: _MACRON[m.group(1)], text)\n    text = re.sub(_MARKERS_CLASS, \"\", text)\n    return text\n</code></pre>\n<p>This category includes: AKT 10, AKT 11b, AKT 5 2008, AKT 7b, and AKT 9a.</p>\n<ul>\n<li><strong>Broken_text type</strong>: Similar to the text type, zooming in does not affect clarity. However, the text layer extracted using PyMuPDF exhibits a one-to-many or many-to-many correspondence with what is visually observed. </li>\n</ul>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18505507%2F05787bbe3e387d112d2845c6df35a3f0%2F4.png?generation=1774319930773772&amp;alt=media\" alt=\"example AKT 8 2015\"></p>\n<p>This category includes: AKT 8 2015 and AKT 7a.</p>\n<p>For the broken_text type, we will directly treat them as the image type.</p>\n<p>Once the file type is identified, we pass text-based PDFs directly to the LLM API for processing, where it extracts the corresponding document pairs (which are later split into sentences). The method involves using Regular Expressions (Regex) to identify heading blocks, then passing both the headings and their corresponding text blocks to a LLM api for analysis and the extraction of document pairs (including error correction).</p>\n<p>For image-based PDFs, our workflow is as follows: \nwe first utilize the <a href=\"https://www.kaggle.com/code/angantyr/text-extraction-from-pdf-docs-using-glm-oc\" target=\"_blank\">open-source GLM-OCR method</a>, featuring custom parameter tuning and parallelization enhancements. Then we can get this:</p>\n<pre><code>{\n    \"p-1_f-0\": \"1. kt 94/k 1263\",\n    \"p-1_f-1\": \"14 1/2 GÍN KB \"KI\" I-lí-dan\",\n    \"p-1_f-2\": \"14 1/2 shekels of silver is owed by Ili-dan\",\n    \"p-1_f-3\": \"DUMU Šu-ki-tim 2 ma-na 10 GÍN / KU\",\n    \"p-1_f-4\": \"son of Sukkutum. 2 minas 10 shekels of\",\n    ...\n}\n</code></pre>\n<table>\n<thead>\n<tr>\n<th></th>\n<th>page_num</th>\n<th>field_num</th>\n<th>x</th>\n<th>y</th>\n<th>w</th>\n<th>h</th>\n<th>x_pad</th>\n<th>y_pad</th>\n<th>w_pad</th>\n<th>h_pad</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n<td>-1</td>\n<td>0</td>\n<td>0</td>\n<td>2075</td>\n<td>2809</td>\n<td>0</td>\n<td>0</td>\n<td>2075</td>\n<td>2809</td>\n</tr>\n<tr>\n<td>1</td>\n<td>1</td>\n<td>0</td>\n<td>864</td>\n<td>253</td>\n<td>378</td>\n<td>50</td>\n<td>852</td>\n<td>241</td>\n<td>402</td>\n<td>74</td>\n</tr>\n<tr>\n<td>2</td>\n<td>1</td>\n<td>1</td>\n<td>166</td>\n<td>403</td>\n<td>533</td>\n<td>48</td>\n<td>154</td>\n<td>391</td>\n<td>557</td>\n<td>72</td>\n</tr>\n<tr>\n<td>3</td>\n<td>1</td>\n<td>2</td>\n<td>1167</td>\n<td>410</td>\n<td>746</td>\n<td>52</td>\n<td>1155</td>\n<td>398</td>\n<td>770</td>\n<td>76</td>\n</tr>\n<tr>\n<td>4</td>\n<td>1</td>\n<td>3</td>\n<td>162</td>\n<td>470</td>\n<td>773</td>\n<td>49</td>\n<td>150</td>\n<td>458</td>\n<td>797</td>\n<td>73</td>\n</tr>\n<tr>\n<td>…</td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n<td></td>\n</tr>\n</tbody>\n</table>\n<p><br></p>\n<p>Next, we use Regex to match the headings again. The LLM API then processes these headings and images to generate text. Finally, we run a second pass with the image and text together for fine-grained correction, ensuring a high degree of accuracy.</p>\n<p>Some of these PDFs are in Turkish. We're using a combination of LLM APIs with dictionaries &amp; Turkish translation to translate them into English for training.</p>\n<hr>\n<h2>Train.csv</h2>\n<p>Regarding <code>train.csv</code>, I have to mention the <strong>OARE</strong> website. For each sample, we visit <code>https://oare.byu.edu/epigraphies/{oare_id}</code>. </p>\n<p>We discovered that clicking on a specific word on this page displays its definition. Consequently, we <strong>scraped</strong> the site to obtain the meanings of these words. While not every word has a corresponding definition, the vast majority do, which is more than sufficient for our needs.</p>\n<p>As a result, we obtained a dictionary covering most of the words in train.csv. </p>\n<p>However, it is important to note that almost all documents in train.csv originate from AKT 5 2008, AKT 6a, AKT 6b, AKT 6c, AKT 6d, AKT 6e, and AKT 8 2015. To improve accuracy, we performed similarity matching between the entries in train.csv and the text extracted from these PDF sources. This allowed us to retrieve the \"ground truth\" or correct form of the documents, as the data quality within the original PDFs is significantly higher. (This involved a lot of manual fixes.)</p>\n<p>Following these procedures, we used that dictionary to give the LLM a general understanding of what transliteration means, then followed the English sentence breaks to eventually obtain sentence-level parallel corpora. (The alignment in this step might not be perfect, so we have implemented corresponding AUG to address this.)</p>\n<hr>\n<h2>Published_texts.csv</h2>\n<p>Similar to train.csv, almost all samples can be found on the OARE website. However, since translations were unavailable, we used an LLM API to create pseudo-labels for these samples. The process is roughly as follows: we prompt the LLM to translate the original text using the dictionary we retrieved from the OARE site.</p>\n<p>example (The prompt also includes several constraints, such as JSON formatting, mana 60 conversion, …):</p>\n<pre><code>key_prompt = \"\"\"\nKIŠIB a-šùr-ma-lik DUMU i-na-a a-na a-šùr-na-da DUMU &lt;gap&gt; ù en-um-a-šùr DUMU &lt;gap&gt; a-pu-tum a-na a-wa-at ṭup-pì-im iḫ-da &lt;gap&gt; ša ú-&lt;gap&gt; ḫa-sí-sú &lt;gap&gt;\n\n{KIŠIB|form:Noun|1. seal, cylinder seal 2. seal impression produced by a cylinder seal 3. sealed clay tablet (legal or administrative document, also letter)}\n{a-šùr-ma-lik|form:Noun|1. Aššur is counselor}\n{DUMU|form:Noun|1. son}\n{i-na-a|form:Noun|nan}\n{a-na|form:Preposition|1. to, for, up to,  toward, against, upon}\n{a-šùr-na-da|form:Noun|1. Praise Aššur!}\n{ù|form:Conjunction|1. and}\n{en-um-a-šùr|form:Noun|1. Mercy O Aššur}\n{a-pu-tum|form:Interjection|1. urgent!, please!}\n{a-wa-at|form:Noun|1. word, matter, lawsuit 2. pl. awâtum - stipulation, rule}\n{ṭup-pì-im|form:Noun|1. (inscribed) tablet (of clay, rarely of other materials) 2. board, flat surface}\n{iḫ-da|form:Finite verb|1. to attend, to watch, to pay attention, to do something carefully 2. to draw someone's attention to something, to alert, to ask someone to pay attention, to take care}\n{ša|form:Pronoun|1. relative pronoun}\n\"\"\"\n</code></pre>\n<hr>\n<h2>Publications.csv</h2>\n<p>For this file, we provide a fixed number of pages to the LLM API to extract sentences and other elements. While this yields a significant amount of data, it is difficult to achieve document-level alignment.</p>\n<hr>\n<p>With that, we have obtained a parallel corpus dataset; the remaining task is now focused on data cleaning.</p>\n<h1>2. Data Processing</h1>\n<p>Actually, the organizers updated the data several times. The <code>train.csv</code> I used in Step 1 to match against the PDF data was the second version, which was much better normalized.</p>\n<p>For preprocessing, here’s the main stuff (mainly for pdf-data or LLM results, The second version of train.csv is already quite clean.):</p>\n<p><strong>General Foundation Processing (Applied to Both Sides)</strong></p>\n<ul>\n<li>Character-level normalization: Apply Unicode NFKC normalization, remove zero-width characters, and unify the formatting of dashes and hyphens.</li>\n<li>Special letter simplification: Convert special characters with diacritics (e.g., Ḫ, ḫ) to standard letters (e.g., H, h).</li>\n<li>Removal of academic editorial symbols: Eliminate various special editorial brackets commonly found in the literature (e.g., ⸢ ⸣ ⌈ ⌉ ⟦ ⟧ [ ], etc.) and footnote superscript numbers.</li>\n<li>Fraction and decimal formatting: Intelligently convert decimals, mixed numbers (e.g., 1 1/2), or regular fractions in the text into compact Unicode single-character fractions (e.g., ½, ⅓, ¾).</li>\n</ul>\n<hr>\n<p><strong>Transliteration Text Side</strong>\nThe primary objective of the source text side is to eliminate typographical variations within the sign system and to standardize determinatives for divine names, toponyms, and similar entities.</p>\n<ul>\n<li>Determinative and script normalization:<ul>\n<li>Convert subscript numbers (e.g., ₀₁₂) to standard digits.</li>\n<li>Unify superscript determinatives (e.g., ᵏⁱ, ᵈUTU) or superscripts indicated by carets (e.g., ^{ki}) into standard curly brace syntax (e.g., {ki}, {d}UTU).</li>\n<li>Intelligently identify and merge determinatives that are separated from proper nouns (e.g., d UTU -&gt; {d}UTU).</li></ul></li>\n<li>Sign composition and placeholder cleanup:<ul>\n<li>Convert multiplication signs (×, ✖) representing sign compounding into periods (.).</li>\n<li>Clean up or convert the unknown sign marker \"x\" attached to the end of a word or before a parenthesis (e.g., SUDx -&gt; SUD).</li></ul></li>\n<li>Removal of specific academic annotations: Remove specifically formatted parenthetical annotations (e.g., editorial notes like \"(over erased)\", \"(met.)\"), standalone line numbers (e.g., \"10.\"), and the pipe character (|) unique to the Oracc system.</li>\n<li>Gap handling and alignment: Unify and normalize various symbols indicating missing text (e.g., consecutive periods \"…\", the standalone letter \"x\", \"missing\", etc.) into a single placeholder <code>&lt;gap&gt;</code>, and strictly clean up extraneous spaces and punctuation before and after the <code>&lt;gap&gt;</code> marker.</li>\n<li>Dictionary-level hard replacement: Erase punctuation marks such as question marks and exclamation points based on the <code>transliteration_map</code>, and correct specific spellings of personal names and anomalous annotations.</li>\n</ul>\n<hr>\n<p><strong>Translation Side</strong>\nThe primary objective of the translation side is to enhance the natural linguistic fluency of the text, eliminating alternative options and academic jargon left by translators.</p>\n<ul>\n<li>Aggressive annotation and markup clearance: Compared to the source text side, the translation side indiscriminately removes all translational explanations within parentheses, while also eliminating asterisks (*) and quotation marks (\") used for emphasis.</li>\n<li>Academic/scribal terminology removal: Automatically identify and delete academic annotations such as \"fem. plur.\" (feminine plural), \"lit.\" (literally), \"sic\" (thus in the original), \"i.e.\", and similar terms.</li>\n<li>Intelligent decision-making for slash alternatives:<ul>\n<li>When an \"either/or\" scenario appears in the translation (e.g., a / the, like / instead, promised / been), the script automatically selects the vocabulary that better fits the context or predefined settings.</li>\n<li>Automatically handle capitalization inheritance (if the original word is capitalized, the selected word will automatically be capitalized).</li></ul></li>\n<li>Roman numeral month conversion: Identify expressions such as \"Month XII\" and convert them to Arabic numerals, such as \"Month 12\".</li>\n<li>Expansion of complex measurement units: Based on the <code>translation_map</code>, expand complex fractional weights unique to antiquity (e.g., 12 5/12 shekels) into more readable text (e.g., 12 ⅓ shekels 15 grains).</li>\n<li>Punctuation and typographical correction: Fix extraneous spaces before punctuation marks (e.g., commas, semicolons) and ensure compactness between numbers and fractions.</li>\n<li>Gap mapping: Similarly, replace \"x\" placeholders or ellipses appearing in the translation with <code>&lt;gap&gt;</code> to ensure strict alignment with the source text side in subsequent machine learning or text comparison processes.</li>\n</ul>\n<hr>\n<p>Actually, this part skips over a considerable amount of manual effort—things like manually checking for anomalies, using regex for matching, and then incorporating those into the cleaning rules, this is our final clean func:</p>\n<p><a href=\"https://www.kaggle.com/code/wscmisx/dpc-clean-function/notebook?scriptVersionId=306066643\" target=\"_blank\">clean function</a></p>\n<h1>3. Training</h1>\n<ul>\n<li><strong>Dynamic Context Sampling</strong></li>\n</ul>\n<p>This technique aims to address two primary issues:</p>\n<ol>\n<li>Potential sentence misalignments during the extraction phase; we intend to make the model less sensitive to sentence alignment.</li>\n<li>The unknown sentence length distribution of the test set, which may differ from our segmented data; we utilize this approach to enable the model to adapt to varying input and output lengths.</li>\n</ol>\n<p>Specifically, we implemented the following mechanisms:</p>\n<ul>\n<li><strong>Zipf / Geometric Mixture Distribution Sampling (K)</strong>: The number of contextual sentences included is dynamically determined for each instance . Following Zipf's law (long-tail distribution), the model predominantly encounters short contexts and occasionally processes extremely long contexts, thereby enhancing its adaptability to varying text lengths.</li>\n<li><strong>Symmetric / Asymmetric Allocation (Symmetric LR)</strong>: The ratio of left to right contextual sentences (L and R) is dynamically determined.</li>\n<li><strong>Random Contextual Sentence Insertion (Random Context)</strong>: There is a 20% probability (<code>random_ctx_prob = 0.20</code>) of bypassing contiguous context and instead randomly sampling and concatenating sentences from the same article. This compels the model to learn not only the local context but also the global topic, preventing the rote memorization of absolute sentence orders.</li>\n</ul>\n<pre><code>def _build_k_cdf(self):\n    \"\"\"\n    Build a CDF over K in [1, k_max].\n    - zipf:     P(K) ∝ 1/K  (heavy-tail, good length coverage)\n    - geom_mix: (1-α)·Geom(p) + α·Zipf(1/K)\n    \"\"\"\n    keys = list(range(1, self.k_max + 1))\n\n    if self.k_sampling == \"geom_mix\":\n        # geometric over {1..k_max}: p*(1-p)^(k-1)\n        geom, zipf = [self.geom_p * ((1.0 - self.geom_p) ** (k - 1)) for k in keys], [1.0 / k for k in keys]\n        probs = [(1.0 - self.long_tail_mix) * g + self.long_tail_mix * z for g, z in zip(geom, zipf)]\n    else:\n        # default: Zipf / 1-K\n        probs = [1.0 / k for k in keys]\n\n    s = sum(probs); probs = [p / s for p in probs]\n    cdf = list(accumulate(probs))\n    cdf[-1] = 1.0\n    return keys, cdf\n\ndef _sample_K(self):\n    r = random.random()\n    i = bisect_left(self._k_cdf, r)\n    if i &gt;= len(self._k_keys): i = len(self._k_keys) - 1\n    return int(self._k_keys[i])\n\n@staticmethod\ndef _sample_LR(K: int):\n    # L ~ Binomial(K-1, 0.5), R = K-1-L\n    if K &lt;= 1: return 0, 0\n    L = int(np.random.binomial(K - 1, 0.5))\n    R = int((K - 1) - L)\n    return L, R\n</code></pre>\n<ul>\n<li><strong>R-Drop</strong></li>\n</ul>\n<p>For a given input x, it is passed through the network twice (due to Dropout, the two forward propagation results, y_1 and y_2, will exhibit minor variances). In addition to calculating the standard cross-entropy loss, the symmetric Kullback-Leibler (KL) divergence between these two output distributions (logits) is computed. This enforces output consistency in the presence of internal noise (Dropout), significantly mitigating overfitting.</p>\n<ul>\n<li><strong>PGD - Projected Gradient Descent</strong></li>\n</ul>\n<p>This involves identifying the \"worst-case perturbations\" within the continuous embedding space and injecting them into the input, ensuring that the model can still produce accurate predictions even under slight adversarial perturbations.</p>\n<ul>\n<li><strong>EMA</strong></li>\n</ul>\n<p>Exponential Moving Average, a commonly adopted technique in deep learning competitions to improve model generalization and stability.</p>\n<ul>\n<li><strong>Engineering Optimizations</strong></li>\n</ul>\n<p>Incorporating these complex components necessitates engineering optimizations, which include:</p>\n<ul>\n<li>DeepSpeed ZeRO-2</li>\n<li>Gradient Checkpointing</li>\n<li>Custom Trainer implementation</li>\n</ul>\n<p>Final parameters:</p>\n<pre><code>k_max=10\nk_sampling=\"zipf\"\ngeom_p=0.35\nrandom_ctx_prob=0.20\nRDROP_ALPHA = 0.8\nPGD_ENABLE = True\nPGD_STEPS  = 3\nPGD_EPS    = 0.5\nPGD_ALPHA  = 0.2\nPGD_EMB_KEYWORDS = (\"shared\", \"embed_tokens\", \"embeddings\")\n</code></pre>\n<h1>4. Experiment</h1>\n<p>This is our primary experimental pipeline (the optimal path for single-mode optimization).\nBy using different subsets of data, we trained a variety of models. Generally, it works like this:</p>\n<table>\n<thead>\n<tr>\n<th>Model</th>\n<th>Method</th>\n<th>LB</th>\n<th>PB</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>byt5-large</td>\n<td>v1 No external data (only aligned and cleaned <code>train.csv</code>)</td>\n<td>34.0</td>\n<td>34.3</td>\n</tr>\n<tr>\n<td>byt5-large</td>\n<td>v2 Added Turkish-to-English data</td>\n<td>36.9</td>\n<td>37.0</td>\n</tr>\n<tr>\n<td>byt5-large</td>\n<td>v3 Added high quality publications data</td>\n<td>38.7</td>\n<td>38.9</td>\n</tr>\n<tr>\n<td>byt5-large</td>\n<td>v4 Pre-trained for 5 epochs using all <code>publications</code> data before formal training (filtered by quality)</td>\n<td>38.6</td>\n<td>39.5</td>\n</tr>\n<tr>\n<td>byt5-large</td>\n<td>v5 Pre-trained for 5 epochs using all <code>publications</code> data and <code>published_texts</code> pseudo-labels before formal training (filtered by quality)</td>\n<td>38.9</td>\n<td>39.3</td>\n</tr>\n<tr>\n<td>byt5-base</td>\n<td>v6 Formally trained using <code>publications</code> data, <code>published_texts</code> pseudo-labels, alongside main data</td>\n<td>39.3</td>\n<td>39.9</td>\n</tr>\n</tbody>\n</table>\n<p><br></p>\n<p>Initially, we thought the extra data was too noisy and left it out of the formal training phase, but it actually performed better once we included it—probably just because of the large volume.</p>\n<h1>5. Inference</h1>\n<p>Our final submission consists of 15 models, primarily detailed as follows:</p>\n<table>\n<thead>\n<tr>\n<th>Model Description</th>\n<th>LB</th>\n<th>PB</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Large model trained on data subset v3</td>\n<td>38.7</td>\n<td>38.9</td>\n</tr>\n<tr>\n<td>Large model trained on data subset v3 with name augmentation</td>\n<td>37.8</td>\n<td>38.8</td>\n</tr>\n<tr>\n<td>XL model trained on data subset v3</td>\n<td>37.9</td>\n<td>38.3</td>\n</tr>\n<tr>\n<td>Large model trained on data subset v4</td>\n<td>38.6</td>\n<td>39.5</td>\n</tr>\n<tr>\n<td>Large model trained on data subset v5</td>\n<td>38.9</td>\n<td>39.3</td>\n</tr>\n<tr>\n<td>Base model trained on data subset v5</td>\n<td>38.8</td>\n<td>39.0</td>\n</tr>\n<tr>\n<td>Base model trained on data subset v6</td>\n<td>39.2</td>\n<td>39.5</td>\n</tr>\n<tr>\n<td>Base model trained on data subset v6 with additional filtering and cleaning (v1)</td>\n<td>39.3</td>\n<td>39.9</td>\n</tr>\n<tr>\n<td>Base model trained on data subset v6 with additional filtering and cleaning (v2)</td>\n<td>38.3</td>\n<td>39.2</td>\n</tr>\n<tr>\n<td>Base model trained on data subset v6 with additional filtering and cleaning (v3)</td>\n<td>38.2</td>\n<td>39.5</td>\n</tr>\n<tr>\n<td>Base model trained on data subset v6 with additional filtering and cleaning (v4)</td>\n<td>38.4</td>\n<td>39.6</td>\n</tr>\n</tbody>\n</table>\n<p>We performed 5-fold cross-validation training based on our best-performing model on the Public Leaderboard (LB score 39.3: Base model trained on data subset v6 with additional filtering and cleaning (v1)). Although these models were not submitted individually, they were also incorporated into the MBR (Minimum Bayes Risk) ensemble.</p>\n<p>To accelerate inference, we restricted the output to four results per model using a beam size of 4 (<code>beam=4</code>), without applying any sampling methods (our experiments indicated that utilizing sampling within the MBR process leads to a decrease in score). </p>\n<p>The models were grouped and deployed across two T4 GPUs for execution.</p>\n<p><img src=\"https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18505507%2F2d32d5e4df1b2bbafc1e969535c8f85f%2Fall_models.png?generation=1774331390404829&amp;alt=media\" alt=\"15models\"></p>\n<p><a href=\"https://www.kaggle.com/code/wscmisx/final-dpc-ensemble-mbr-infer\" target=\"_blank\"><strong>Final Score:</strong> 40.7  |  <strong>Execution Time:</strong> 208m 50s</a></p>\n<h1>6. What didn't work</h1>\n<ul>\n<li><strong>Organizing the current target sentence and its context like this (during both training and inference):</strong></li>\n</ul>\n<pre><code>translate Akkadian to English. Translate TARGET only. Use CONTEXT only as reference.\nTARGET:\nki-ma mup-pì-ni ta-áa-me-a-ni a-ma-kam lu a-na aí-mì-im a-na É.GAL-lim i-dí-in lu té-ra-at É.GAL-lim ú-kà-lim lu na-aí-ma a-dí-ni lá i-dí-in ma-lá KÙ.AN na-áa-ú ni-bi„-it a-aí-im au-um-au ú au-mì a-bi„-au i-na mup-pì-im lu-up-ta-nim-ma ia-tí aí-ip-ri-ni aé-bi„-lá-nim\n\nCONTEXT BEFORE:\num-ma kà-ru-um kà-ni-ia-ma a-na aa-qí-il… da-tim aí-ip-ri-ni kà-ar kà-ar-ma ú wa-bar-ra-tim qí-bi„-ma mup-pu-um aa a-lim(ki) i-li-kam i-na mup-pì-im aa a-lim(ki) ia-tù u„-mì-im a-nim ma-ma-an KÙ.AN i-aa-ú-mu-ni i-na né-mì-lim da-aùr ú-lá e-WA ia-ra-tí-au kà-ru-um kà-ni-ia i-lá-qé\n\nCONTEXT AFTER:\nme-+e-er mup-pì-ni a-na kà-ar kà-ar-ma ú wa-bar-ra-tim aé-bi„-lá KÙ.AN lu a-na DAM.GÀR-ru-tim i-dí-in au-mì a-wi-lim lu-up-ta-nim\n</code></pre>\n<ul>\n<li><p><strong>Data augmentation on names and dictionaries in the source text during training</strong>\nRandomly altering the capitalization of names, and using a dictionary to randomly select a <code>norm</code> form for many-to-one <code>norm</code> -&gt; <code>lexeme</code> mappings. \nThe single-model score didn't improve much, but we still included these in our ensemble.</p></li>\n<li><p><strong>Weight fusion / averaging</strong>\nThis showed almost no improvement and even resulted in lower scores than the original single model. After a few attempts, we stopped trying this and switched to using MBR (Minimum Bayes Risk) for ensembling instead.</p></li>\n<li><p><strong>Setting a very long <code>maxlen</code> and training at the document level</strong>\nUnsurprisingly, the results were poor because the test set is evaluated at the sentence level.</p></li>\n<li><p><strong>Using Large Language Models (Qwen, LLaMA, etc.)</strong>\nThis was likely due to the tokenizer or the fact that my data wasn't clean enough in the early stages of the competition. Without applying any special tricks, the performance of a trained 14B model was only average. However, I was very glad to see top-ranking participants successfully taking on the challenge using LLMs—an excellent solution!</p></li>\n<li><p><strong>Bidirectional training (adding <code>akk-&gt;eng</code>)</strong>\nThere wasn't much progress in terms of single-model improvement. We didn't have extra submission quotas to test this before ensembling, but perhaps including it in the ensemble could have provided a slight boost to the final results.</p></li>\n</ul>",
      "rawMarkdown": "First of all, I’d like to thank my teammate [@jackvd](https://www.kaggle.com/jackvd) for providing high-quality data and clever data cleaning methods, as well as for constantly bringing fresh ideas to the table. I also want to thank [@marisyn](https://www.kaggle.com/marisyn) for providing numerous excellent APIs and handling the PDF extraction work! And all personnel from the host organization, DPI.\n\nOur solution focuses on three key areas: data, training, and inference.\n\n# 1. Data Extract\n\n## PDF Data\n\nThe organizers provided a large number of [PDFs](https://www.kaggle.com/datasets/deeppast/old-assyrian-kltepe-tablets-in-pdf) (There is also a *Larsen 2002* in [here](https://www.kaggle.com/datasets/deeppast/old-assyrian-grammars-and-other-resources).), so our first challenge was figuring out how to effectively extract text from them.\n\nAfter reviewing these PDFs, we have concluded that they can primarily be categorized into three types: image, text, and broken_text:\n\n- **Image type**: Although these have an OCR text layer, OCR artifacts become clearly visible when zooming in on the PDF. This category includes: AKT 1 1990, AKT 11a, AKT 2 1995, AKT 3 1995, AKT 4 2006, AKT 6a, AKT 6b, AKT 6c, AKT 6d, AKT 6e, and AKT_12.\n\n- **Text type**: Zooming in does not degrade the clarity of the PDF in any way. Furthermore, the text layer extracted using PyMuPDF has an exact one-to-one correspondence with what is visible to the naked eye. For example, using this code to handle one-to-one character error issues:\n```python\n# AKT 5 2008\n_MACRON = {\n    \"a\": \"ā\", \"e\": \"ē\", \"i\": \"ī\", \"u\": \"ū\",\n    \"A\": \"Ā\", \"E\": \"Ē\", \"I\": \"Ī\", \"U\": \"Ū\",\n}\n_MARKERS_CLASS = r\"[\\u00a3\\u2039\\u2122]+\"          # £‹™ (allow multi)\n_MARKER_VOWEL_RE = re.compile(_MARKERS_CLASS + r\"([aeiuAEIU])\")\n\ndef apply_akt5_vowel_marker_rule(text: str) -> str:\n    text = _MARKER_VOWEL_RE.sub(lambda m: _MACRON[m.group(1)], text)\n    text = re.sub(_MARKERS_CLASS, \"\", text)\n    return text\n```\nThis category includes: AKT 10, AKT 11b, AKT 5 2008, AKT 7b, and AKT 9a.\n\n- **Broken_text type**: Similar to the text type, zooming in does not affect clarity. However, the text layer extracted using PyMuPDF exhibits a one-to-many or many-to-many correspondence with what is visually observed. \n\n![example AKT 8 2015](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18505507%2F05787bbe3e387d112d2845c6df35a3f0%2F4.png?generation=1774319930773772&alt=media)\n\n\nThis category includes: AKT 8 2015 and AKT 7a.\n\nFor the broken_text type, we will directly treat them as the image type.\n\nOnce the file type is identified, we pass text-based PDFs directly to the LLM API for processing, where it extracts the corresponding document pairs (which are later split into sentences). The method involves using Regular Expressions (Regex) to identify heading blocks, then passing both the headings and their corresponding text blocks to a LLM api for analysis and the extraction of document pairs (including error correction).\n\nFor image-based PDFs, our workflow is as follows: \nwe first utilize the [open-source GLM-OCR method](https://www.kaggle.com/code/angantyr/text-extraction-from-pdf-docs-using-glm-oc), featuring custom parameter tuning and parallelization enhancements. Then we can get this:\n```python\n{\n    \"p-1_f-0\": \"1. kt 94/k 1263\",\n    \"p-1_f-1\": \"14 1/2 GÍN KB \"KI\" I-lí-dan\",\n    \"p-1_f-2\": \"14 1/2 shekels of silver is owed by Ili-dan\",\n    \"p-1_f-3\": \"DUMU Šu-ki-tim 2 ma-na 10 GÍN / KU\",\n    \"p-1_f-4\": \"son of Sukkutum. 2 minas 10 shekels of\",\n    ...\n}\n```\n|    |   page_num |   field_num |    x |   y |    w |    h |   x_pad |   y_pad |   w_pad |   h_pad |\n|---:|-----------:|------------:|-----:|----:|-----:|-----:|--------:|--------:|--------:|--------:|\n|  0 |          1 |          -1 |    0 |   0 | 2075 | 2809 |       0 |       0 |    2075 |    2809 |\n|  1 |          1 |           0 |  864 | 253 |  378 |   50 |     852 |     241 |     402 |      74 |\n|  2 |          1 |           1 |  166 | 403 |  533 |   48 |     154 |     391 |     557 |      72 |\n|  3 |          1 |           2 | 1167 | 410 |  746 |   52 |    1155 |     398 |     770 |      76 |\n|  4 |          1 |           3 |  162 | 470 |  773 |   49 |     150 |     458 |     797 |      73 |\n|  ... |\n\n<br>\n\nNext, we use Regex to match the headings again. The LLM API then processes these headings and images to generate text. Finally, we run a second pass with the image and text together for fine-grained correction, ensuring a high degree of accuracy.\n\nSome of these PDFs are in Turkish. We're using a combination of LLM APIs with dictionaries & Turkish translation to translate them into English for training.\n\n---\n\n## Train.csv\nRegarding `train.csv`, I have to mention the **OARE** website. For each sample, we visit `https://oare.byu.edu/epigraphies/{oare_id}`. \n\nWe discovered that clicking on a specific word on this page displays its definition. Consequently, we **scraped** the site to obtain the meanings of these words. While not every word has a corresponding definition, the vast majority do, which is more than sufficient for our needs.\n\nAs a result, we obtained a dictionary covering most of the words in train.csv. \n\nHowever, it is important to note that almost all documents in train.csv originate from AKT 5 2008, AKT 6a, AKT 6b, AKT 6c, AKT 6d, AKT 6e, and AKT 8 2015. To improve accuracy, we performed similarity matching between the entries in train.csv and the text extracted from these PDF sources. This allowed us to retrieve the \"ground truth\" or correct form of the documents, as the data quality within the original PDFs is significantly higher. (This involved a lot of manual fixes.)\n\nFollowing these procedures, we used that dictionary to give the LLM a general understanding of what transliteration means, then followed the English sentence breaks to eventually obtain sentence-level parallel corpora. (The alignment in this step might not be perfect, so we have implemented corresponding AUG to address this.)\n\n---\n\n## Published_texts.csv\n\nSimilar to train.csv, almost all samples can be found on the OARE website. However, since translations were unavailable, we used an LLM API to create pseudo-labels for these samples. The process is roughly as follows: we prompt the LLM to translate the original text using the dictionary we retrieved from the OARE site.\n\nexample (The prompt also includes several constraints, such as JSON formatting, mana 60 conversion, ...):\n```\nkey_prompt = \"\"\"\nKIŠIB a-šùr-ma-lik DUMU i-na-a a-na a-šùr-na-da DUMU <gap> ù en-um-a-šùr DUMU <gap> a-pu-tum a-na a-wa-at ṭup-pì-im iḫ-da <gap> ša ú-<gap> ḫa-sí-sú <gap>\n\n{KIŠIB|form:Noun|1. seal, cylinder seal 2. seal impression produced by a cylinder seal 3. sealed clay tablet (legal or administrative document, also letter)}\n{a-šùr-ma-lik|form:Noun|1. Aššur is counselor}\n{DUMU|form:Noun|1. son}\n{i-na-a|form:Noun|nan}\n{a-na|form:Preposition|1. to, for, up to,  toward, against, upon}\n{a-šùr-na-da|form:Noun|1. Praise Aššur!}\n{ù|form:Conjunction|1. and}\n{en-um-a-šùr|form:Noun|1. Mercy O Aššur}\n{a-pu-tum|form:Interjection|1. urgent!, please!}\n{a-wa-at|form:Noun|1. word, matter, lawsuit 2. pl. awâtum - stipulation, rule}\n{ṭup-pì-im|form:Noun|1. (inscribed) tablet (of clay, rarely of other materials) 2. board, flat surface}\n{iḫ-da|form:Finite verb|1. to attend, to watch, to pay attention, to do something carefully 2. to draw someone's attention to something, to alert, to ask someone to pay attention, to take care}\n{ša|form:Pronoun|1. relative pronoun}\n\"\"\"\n```\n\n---\n\n## Publications.csv\n\nFor this file, we provide a fixed number of pages to the LLM API to extract sentences and other elements. While this yields a significant amount of data, it is difficult to achieve document-level alignment.\n\n---\n\nWith that, we have obtained a parallel corpus dataset; the remaining task is now focused on data cleaning.\n\n# 2. Data Processing\n\nActually, the organizers updated the data several times. The `train.csv` I used in Step 1 to match against the PDF data was the second version, which was much better normalized.\n\nFor preprocessing, here’s the main stuff (mainly for pdf-data or LLM results, The second version of train.csv is already quite clean.):\n\n**General Foundation Processing (Applied to Both Sides)**\n- Character-level normalization: Apply Unicode NFKC normalization, remove zero-width characters, and unify the formatting of dashes and hyphens.\n- Special letter simplification: Convert special characters with diacritics (e.g., Ḫ, ḫ) to standard letters (e.g., H, h).\n- Removal of academic editorial symbols: Eliminate various special editorial brackets commonly found in the literature (e.g., ⸢ ⸣ ⌈ ⌉ ⟦ ⟧ [ ], etc.) and footnote superscript numbers.\n- Fraction and decimal formatting: Intelligently convert decimals, mixed numbers (e.g., 1 1/2), or regular fractions in the text into compact Unicode single-character fractions (e.g., ½, ⅓, ¾).\n\n---\n\n**Transliteration Text Side**\nThe primary objective of the source text side is to eliminate typographical variations within the sign system and to standardize determinatives for divine names, toponyms, and similar entities.\n- Determinative and script normalization:\n  - Convert subscript numbers (e.g., ₀₁₂) to standard digits.\n  - Unify superscript determinatives (e.g., ᵏⁱ, ᵈUTU) or superscripts indicated by carets (e.g., ^{ki}) into standard curly brace syntax (e.g., {ki}, {d}UTU).\n  - Intelligently identify and merge determinatives that are separated from proper nouns (e.g., d UTU -> {d}UTU).\n- Sign composition and placeholder cleanup:\n  - Convert multiplication signs (×, ✖) representing sign compounding into periods (.).\n  - Clean up or convert the unknown sign marker \"x\" attached to the end of a word or before a parenthesis (e.g., SUDx -> SUD).\n- Removal of specific academic annotations: Remove specifically formatted parenthetical annotations (e.g., editorial notes like \"(over erased)\", \"(met.)\"), standalone line numbers (e.g., \"10.\"), and the pipe character (|) unique to the Oracc system.\n- Gap handling and alignment: Unify and normalize various symbols indicating missing text (e.g., consecutive periods \"...\", the standalone letter \"x\", \"missing\", etc.) into a single placeholder `<gap>`, and strictly clean up extraneous spaces and punctuation before and after the `<gap>` marker.\n- Dictionary-level hard replacement: Erase punctuation marks such as question marks and exclamation points based on the `transliteration_map`, and correct specific spellings of personal names and anomalous annotations.\n\n---\n\n**Translation Side**\nThe primary objective of the translation side is to enhance the natural linguistic fluency of the text, eliminating alternative options and academic jargon left by translators.\n- Aggressive annotation and markup clearance: Compared to the source text side, the translation side indiscriminately removes all translational explanations within parentheses, while also eliminating asterisks (*) and quotation marks (\") used for emphasis.\n- Academic/scribal terminology removal: Automatically identify and delete academic annotations such as \"fem. plur.\" (feminine plural), \"lit.\" (literally), \"sic\" (thus in the original), \"i.e.\", and similar terms.\n- Intelligent decision-making for slash alternatives:\n  - When an \"either/or\" scenario appears in the translation (e.g., a / the, like / instead, promised / been), the script automatically selects the vocabulary that better fits the context or predefined settings.\n  - Automatically handle capitalization inheritance (if the original word is capitalized, the selected word will automatically be capitalized).\n- Roman numeral month conversion: Identify expressions such as \"Month XII\" and convert them to Arabic numerals, such as \"Month 12\".\n- Expansion of complex measurement units: Based on the `translation_map`, expand complex fractional weights unique to antiquity (e.g., 12 5/12 shekels) into more readable text (e.g., 12 ⅓ shekels 15 grains).\n- Punctuation and typographical correction: Fix extraneous spaces before punctuation marks (e.g., commas, semicolons) and ensure compactness between numbers and fractions.\n- Gap mapping: Similarly, replace \"x\" placeholders or ellipses appearing in the translation with `<gap>` to ensure strict alignment with the source text side in subsequent machine learning or text comparison processes.\n\n---\n\nActually, this part skips over a considerable amount of manual effort—things like manually checking for anomalies, using regex for matching, and then incorporating those into the cleaning rules, this is our final clean func:\n\n[clean function](https://www.kaggle.com/code/wscmisx/dpc-clean-function/notebook?scriptVersionId=306066643)\n\n# 3. Training\n- **Dynamic Context Sampling**\n\nThis technique aims to address two primary issues:\n\n1. Potential sentence misalignments during the extraction phase; we intend to make the model less sensitive to sentence alignment.\n2. The unknown sentence length distribution of the test set, which may differ from our segmented data; we utilize this approach to enable the model to adapt to varying input and output lengths.\n\nSpecifically, we implemented the following mechanisms:\n\n- **Zipf / Geometric Mixture Distribution Sampling (K)**: The number of contextual sentences included is dynamically determined for each instance . Following Zipf's law (long-tail distribution), the model predominantly encounters short contexts and occasionally processes extremely long contexts, thereby enhancing its adaptability to varying text lengths.\n- **Symmetric / Asymmetric Allocation (Symmetric LR)**: The ratio of left to right contextual sentences (L and R) is dynamically determined.\n- **Random Contextual Sentence Insertion (Random Context)**: There is a 20% probability (`random_ctx_prob = 0.20`) of bypassing contiguous context and instead randomly sampling and concatenating sentences from the same article. This compels the model to learn not only the local context but also the global topic, preventing the rote memorization of absolute sentence orders.\n```\ndef _build_k_cdf(self):\n    \"\"\"\n    Build a CDF over K in [1, k_max].\n    - zipf:     P(K) ∝ 1/K  (heavy-tail, good length coverage)\n    - geom_mix: (1-α)·Geom(p) + α·Zipf(1/K)\n    \"\"\"\n    keys = list(range(1, self.k_max + 1))\n\n    if self.k_sampling == \"geom_mix\":\n        # geometric over {1..k_max}: p*(1-p)^(k-1)\n        geom, zipf = [self.geom_p * ((1.0 - self.geom_p) ** (k - 1)) for k in keys], [1.0 / k for k in keys]\n        probs = [(1.0 - self.long_tail_mix) * g + self.long_tail_mix * z for g, z in zip(geom, zipf)]\n    else:\n        # default: Zipf / 1-K\n        probs = [1.0 / k for k in keys]\n\n    s = sum(probs); probs = [p / s for p in probs]\n    cdf = list(accumulate(probs))\n    cdf[-1] = 1.0\n    return keys, cdf\n\ndef _sample_K(self):\n    r = random.random()\n    i = bisect_left(self._k_cdf, r)\n    if i >= len(self._k_keys): i = len(self._k_keys) - 1\n    return int(self._k_keys[i])\n\n@staticmethod\ndef _sample_LR(K: int):\n    # L ~ Binomial(K-1, 0.5), R = K-1-L\n    if K <= 1: return 0, 0\n    L = int(np.random.binomial(K - 1, 0.5))\n    R = int((K - 1) - L)\n    return L, R\n```\n- **R-Drop**\n\nFor a given input x, it is passed through the network twice (due to Dropout, the two forward propagation results, y_1 and y_2, will exhibit minor variances). In addition to calculating the standard cross-entropy loss, the symmetric Kullback-Leibler (KL) divergence between these two output distributions (logits) is computed. This enforces output consistency in the presence of internal noise (Dropout), significantly mitigating overfitting.\n\n- **PGD - Projected Gradient Descent**\n\nThis involves identifying the \"worst-case perturbations\" within the continuous embedding space and injecting them into the input, ensuring that the model can still produce accurate predictions even under slight adversarial perturbations.\n\n- **EMA**\n\nExponential Moving Average, a commonly adopted technique in deep learning competitions to improve model generalization and stability.\n\n- **Engineering Optimizations**\n\nIncorporating these complex components necessitates engineering optimizations, which include:\n\n- DeepSpeed ZeRO-2\n- Gradient Checkpointing\n- Custom Trainer implementation\n\nFinal parameters:\n```python\nk_max=10\nk_sampling=\"zipf\"\ngeom_p=0.35\nrandom_ctx_prob=0.20\nRDROP_ALPHA = 0.8\nPGD_ENABLE = True\nPGD_STEPS  = 3\nPGD_EPS    = 0.5\nPGD_ALPHA  = 0.2\nPGD_EMB_KEYWORDS = (\"shared\", \"embed_tokens\", \"embeddings\")\n```\n\n\n# 4. Experiment\nThis is our primary experimental pipeline (the optimal path for single-mode optimization).\nBy using different subsets of data, we trained a variety of models. Generally, it works like this:\n\n| Model | Method | LB | PB |\n| :--- | :--- | :--- | :--- |\n| byt5-large | v1 No external data (only aligned and cleaned `train.csv`) | 34.0 | 34.3 |\n| byt5-large | v2 Added Turkish-to-English data | 36.9 | 37.0 |\n| byt5-large | v3 Added high quality publications data | 38.7 | 38.9 |\n| byt5-large | v4 Pre-trained for 5 epochs using all `publications` data before formal training (filtered by quality) | 38.6 | 39.5 |\n| byt5-large | v5 Pre-trained for 5 epochs using all `publications` data and `published_texts` pseudo-labels before formal training (filtered by quality) | 38.9 | 39.3 |\n| byt5-base | v6 Formally trained using `publications` data, `published_texts` pseudo-labels, alongside main data | 39.3 | 39.9 |\n\n<br>\n\nInitially, we thought the extra data was too noisy and left it out of the formal training phase, but it actually performed better once we included it—probably just because of the large volume.\n\n# 5. Inference\n\nOur final submission consists of 15 models, primarily detailed as follows:\n\n| Model Description | LB | PB |\n| :--- | :--- | :--- |\n| Large model trained on data subset v3 | 38.7 | 38.9 |\n| Large model trained on data subset v3 with name augmentation | 37.8 | 38.8 |\n| XL model trained on data subset v3 | 37.9 | 38.3 |\n| Large model trained on data subset v4 | 38.6 | 39.5 |\n| Large model trained on data subset v5 | 38.9 | 39.3 |\n| Base model trained on data subset v5 | 38.8 | 39.0 |\n| Base model trained on data subset v6 | 39.2 | 39.5 |\n| Base model trained on data subset v6 with additional filtering and cleaning (v1) | 39.3 | 39.9 |\n| Base model trained on data subset v6 with additional filtering and cleaning (v2) | 38.3 | 39.2 |\n| Base model trained on data subset v6 with additional filtering and cleaning (v3) | 38.2 | 39.5 |\n| Base model trained on data subset v6 with additional filtering and cleaning (v4) | 38.4 | 39.6 |\n\nWe performed 5-fold cross-validation training based on our best-performing model on the Public Leaderboard (LB score 39.3: Base model trained on data subset v6 with additional filtering and cleaning (v1)). Although these models were not submitted individually, they were also incorporated into the MBR (Minimum Bayes Risk) ensemble.\n\nTo accelerate inference, we restricted the output to four results per model using a beam size of 4 (`beam=4`), without applying any sampling methods (our experiments indicated that utilizing sampling within the MBR process leads to a decrease in score). \n\nThe models were grouped and deployed across two T4 GPUs for execution.\n\n![15models](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18505507%2F2d32d5e4df1b2bbafc1e969535c8f85f%2Fall_models.png?generation=1774331390404829&alt=media)\n\n[**Final Score:** 40.7  |  **Execution Time:** 208m 50s](https://www.kaggle.com/code/wscmisx/final-dpc-ensemble-mbr-infer)\n\n\n# 6. What didn't work\n\n- **Organizing the current target sentence and its context like this (during both training and inference):**\n```text\ntranslate Akkadian to English. Translate TARGET only. Use CONTEXT only as reference.\nTARGET:\nki-ma mup-pì-ni ta-áa-me-a-ni a-ma-kam lu a-na aí-mì-im a-na É.GAL-lim i-dí-in lu té-ra-at É.GAL-lim ú-kà-lim lu na-aí-ma a-dí-ni lá i-dí-in ma-lá KÙ.AN na-áa-ú ni-bi„-it a-aí-im au-um-au ú au-mì a-bi„-au i-na mup-pì-im lu-up-ta-nim-ma ia-tí aí-ip-ri-ni aé-bi„-lá-nim\n\nCONTEXT BEFORE:\num-ma kà-ru-um kà-ni-ia-ma a-na aa-qí-il… da-tim aí-ip-ri-ni kà-ar kà-ar-ma ú wa-bar-ra-tim qí-bi„-ma mup-pu-um aa a-lim(ki) i-li-kam i-na mup-pì-im aa a-lim(ki) ia-tù u„-mì-im a-nim ma-ma-an KÙ.AN i-aa-ú-mu-ni i-na né-mì-lim da-aùr ú-lá e-WA ia-ra-tí-au kà-ru-um kà-ni-ia i-lá-qé\n\nCONTEXT AFTER:\nme-+e-er mup-pì-ni a-na kà-ar kà-ar-ma ú wa-bar-ra-tim aé-bi„-lá KÙ.AN lu a-na DAM.GÀR-ru-tim i-dí-in au-mì a-wi-lim lu-up-ta-nim\n```\n\n- **Data augmentation on names and dictionaries in the source text during training**\nRandomly altering the capitalization of names, and using a dictionary to randomly select a `norm` form for many-to-one `norm` -> `lexeme` mappings. \nThe single-model score didn't improve much, but we still included these in our ensemble.\n\n- **Weight fusion / averaging**\nThis showed almost no improvement and even resulted in lower scores than the original single model. After a few attempts, we stopped trying this and switched to using MBR (Minimum Bayes Risk) for ensembling instead.\n\n- **Setting a very long `maxlen` and training at the document level**\nUnsurprisingly, the results were poor because the test set is evaluated at the sentence level.\n\n- **Using Large Language Models (Qwen, LLaMA, etc.)**\nThis was likely due to the tokenizer or the fact that my data wasn't clean enough in the early stages of the competition. Without applying any special tricks, the performance of a trained 14B model was only average. However, I was very glad to see top-ranking participants successfully taking on the challenge using LLMs—an excellent solution!\n\n- **Bidirectional training (adding `akk->eng`)**\nThere wasn't much progress in terms of single-model improvement. We didn't have extra submission quotas to test this before ensembling, but perhaps including it in the ensemble could have provided a slight boost to the final results.",
      "votes": null
    },
    {
      "id": "3427499",
      "postDate": "03/24/2026 06:51:14",
      "content": "<p>Congratulations! The thoroughness and patience you demonstrated in data processing and augmentation are truly amazing ! I’ve learned so much from it.  </p>",
      "rawMarkdown": "Congratulations! The thoroughness and patience you demonstrated in data processing and augmentation are truly amazing ! I’ve learned so much from it.",
      "votes": null
    },
    {
      "id": "3427513",
      "postDate": "03/24/2026 07:20:42",
      "content": "<p>True, this is actually just the tip of the iceberg. The countless hours of manual work, trial and error, and constant tweaking my teammates and I went through didn't even make it into this writeup.</p>",
      "rawMarkdown": "True, this is actually just the tip of the iceberg. The countless hours of manual work, trial and error, and constant tweaking my teammates and I went through didn't even make it into this writeup.",
      "votes": null
    },
    {
      "id": "3427514",
      "postDate": "03/24/2026 07:21:23",
      "content": "<p>Congratulations! Excellent solution sharing.</p>",
      "rawMarkdown": "Congratulations! Excellent solution sharing.",
      "votes": null
    },
    {
      "id": "3427520",
      "postDate": "03/24/2026 07:25:30",
      "content": "<p>Thank you!</p>",
      "rawMarkdown": "Thank you!",
      "votes": null
    },
    {
      "id": "3427532",
      "postDate": "03/24/2026 07:49:01",
      "content": "<p>I'm glad you found the OCR solution useful!</p>\n<p>Kudos on the great job of testing so many options and getting a 6th place gold!</p>",
      "rawMarkdown": "I'm glad you found the OCR solution useful!\n\nKudos on the great job of testing so many options and getting a 6th place gold!",
      "votes": null
    },
    {
      "id": "3427533",
      "postDate": "03/24/2026 07:50:01",
      "content": "<p>Your code has been a huge help to us! We’re using LLMs to refine the details after the initial extraction.</p>",
      "rawMarkdown": "Your code has been a huge help to us! We’re using LLMs to refine the details after the initial extraction.",
      "votes": null
    },
    {
      "id": "3427595",
      "postDate": "03/24/2026 09:23:39",
      "content": "<p>Congratulations to your team on winning the gold medal. The amount of engineering work is truly astonishing, and congratulations as well on your promotion to Master!</p>",
      "rawMarkdown": "Congratulations to your team on winning the gold medal. The amount of engineering work is truly astonishing, and congratulations as well on your promotion to Master!",
      "votes": null
    },
    {
      "id": "3427597",
      "postDate": "03/24/2026 09:25:35",
      "content": "<p>Thank you! I hope you become a master soon, too!</p>",
      "rawMarkdown": "Thank you! I hope you become a master soon, too!",
      "votes": null
    },
    {
      "id": "3427675",
      "postDate": "03/24/2026 11:47:32",
      "content": "<p>Brilliant work！What a fucking feat for 3G！</p>",
      "rawMarkdown": "Brilliant work！What a fucking feat for 3G！",
      "votes": null
    },
    {
      "id": "3427988",
      "postDate": "03/24/2026 18:52:27",
      "content": "<p>Congrats <a href=\"https://www.kaggle.com/wscmisx\" target=\"_blank\">@wscmisx</a> !!</p>",
      "rawMarkdown": "Congrats @wscmisx !!",
      "votes": null
    },
    {
      "id": "3428078",
      "postDate": "03/24/2026 23:25:21",
      "content": "<p>Thank you so much bro🥰</p>",
      "rawMarkdown": "Thank you so much bro🥰",
      "votes": null
    },
    {
      "id": "3430291",
      "postDate": "03/28/2026 07:31:02",
      "content": "<p>Wow, Thats a genius thinking. Well deserved brother. Congratulations 🎊 </p>",
      "rawMarkdown": "Wow, Thats a genius thinking. Well deserved brother. Congratulations 🎊",
      "votes": null
    }
  ],
  "comments": [
    {
      "id": 3427499,
      "author_name": "brucewu1200",
      "author_url": "",
      "post_date": "03/24/2026 06:51:14",
      "content": "<p>Congratulations! The thoroughness and patience you demonstrated in data processing and augmentation are truly amazing ! I’ve learned so much from it.  </p>",
      "votes": null,
      "replies": [
        {
          "id": 3427513,
          "author_name": "wscmisx",
          "author_url": "",
          "post_date": "03/24/2026 07:20:42",
          "content": "<p>True, this is actually just the tip of the iceberg. The countless hours of manual work, trial and error, and constant tweaking my teammates and I went through didn't even make it into this writeup.</p>",
          "votes": null,
          "replies": []
        }
      ]
    },
    {
      "id": 3427514,
      "author_name": "yaokaixinya123",
      "author_url": "",
      "post_date": "03/24/2026 07:21:23",
      "content": "<p>Congratulations! Excellent solution sharing.</p>",
      "votes": null,
      "replies": [
        {
          "id": 3427520,
          "author_name": "wscmisx",
          "author_url": "",
          "post_date": "03/24/2026 07:25:30",
          "content": "<p>Thank you!</p>",
          "votes": null,
          "replies": []
        }
      ]
    },
    {
      "id": 3427532,
      "author_name": "angantyr",
      "author_url": "",
      "post_date": "03/24/2026 07:49:01",
      "content": "<p>I'm glad you found the OCR solution useful!</p>\n<p>Kudos on the great job of testing so many options and getting a 6th place gold!</p>",
      "votes": null,
      "replies": [
        {
          "id": 3427533,
          "author_name": "wscmisx",
          "author_url": "",
          "post_date": "03/24/2026 07:50:01",
          "content": "<p>Your code has been a huge help to us! We’re using LLMs to refine the details after the initial extraction.</p>",
          "votes": null,
          "replies": []
        }
      ]
    },
    {
      "id": 3427595,
      "author_name": "gunianxue",
      "author_url": "",
      "post_date": "03/24/2026 09:23:39",
      "content": "<p>Congratulations to your team on winning the gold medal. The amount of engineering work is truly astonishing, and congratulations as well on your promotion to Master!</p>",
      "votes": null,
      "replies": [
        {
          "id": 3427597,
          "author_name": "wscmisx",
          "author_url": "",
          "post_date": "03/24/2026 09:25:35",
          "content": "<p>Thank you! I hope you become a master soon, too!</p>",
          "votes": null,
          "replies": []
        }
      ]
    },
    {
      "id": 3427675,
      "author_name": "yxw666",
      "author_url": "",
      "post_date": "03/24/2026 11:47:32",
      "content": "<p>Brilliant work！What a fucking feat for 3G！</p>",
      "votes": null,
      "replies": []
    },
    {
      "id": 3427988,
      "author_name": "alejopaullier",
      "author_url": "",
      "post_date": "03/24/2026 18:52:27",
      "content": "<p>Congrats <a href=\"https://www.kaggle.com/wscmisx\" target=\"_blank\">@wscmisx</a> !!</p>",
      "votes": null,
      "replies": [
        {
          "id": 3428078,
          "author_name": "wscmisx",
          "author_url": "",
          "post_date": "03/24/2026 23:25:21",
          "content": "<p>Thank you so much bro🥰</p>",
          "votes": null,
          "replies": []
        }
      ]
    },
    {
      "id": 3430291,
      "author_name": "koushikrudra",
      "author_url": "",
      "post_date": "03/28/2026 07:31:02",
      "content": "<p>Wow, Thats a genius thinking. Well deserved brother. Congratulations 🎊 </p>",
      "votes": null,
      "replies": []
    }
  ],
  "raw_markdown_by_id": {
    "3427476": "First of all, I’d like to thank my teammate [@jackvd](https://www.kaggle.com/jackvd) for providing high-quality data and clever data cleaning methods, as well as for constantly bringing fresh ideas to the table. I also want to thank [@marisyn](https://www.kaggle.com/marisyn) for providing numerous excellent APIs and handling the PDF extraction work! And all personnel from the host organization, DPI.\n\nOur solution focuses on three key areas: data, training, and inference.\n\n# 1. Data Extract\n\n## PDF Data\n\nThe organizers provided a large number of [PDFs](https://www.kaggle.com/datasets/deeppast/old-assyrian-kltepe-tablets-in-pdf) (There is also a *Larsen 2002* in [here](https://www.kaggle.com/datasets/deeppast/old-assyrian-grammars-and-other-resources).), so our first challenge was figuring out how to effectively extract text from them.\n\nAfter reviewing these PDFs, we have concluded that they can primarily be categorized into three types: image, text, and broken_text:\n\n- **Image type**: Although these have an OCR text layer, OCR artifacts become clearly visible when zooming in on the PDF. This category includes: AKT 1 1990, AKT 11a, AKT 2 1995, AKT 3 1995, AKT 4 2006, AKT 6a, AKT 6b, AKT 6c, AKT 6d, AKT 6e, and AKT_12.\n\n- **Text type**: Zooming in does not degrade the clarity of the PDF in any way. Furthermore, the text layer extracted using PyMuPDF has an exact one-to-one correspondence with what is visible to the naked eye. For example, using this code to handle one-to-one character error issues:\n```python\n# AKT 5 2008\n_MACRON = {\n    \"a\": \"ā\", \"e\": \"ē\", \"i\": \"ī\", \"u\": \"ū\",\n    \"A\": \"Ā\", \"E\": \"Ē\", \"I\": \"Ī\", \"U\": \"Ū\",\n}\n_MARKERS_CLASS = r\"[\\u00a3\\u2039\\u2122]+\"          # £‹™ (allow multi)\n_MARKER_VOWEL_RE = re.compile(_MARKERS_CLASS + r\"([aeiuAEIU])\")\n\ndef apply_akt5_vowel_marker_rule(text: str) -> str:\n    text = _MARKER_VOWEL_RE.sub(lambda m: _MACRON[m.group(1)], text)\n    text = re.sub(_MARKERS_CLASS, \"\", text)\n    return text\n```\nThis category includes: AKT 10, AKT 11b, AKT 5 2008, AKT 7b, and AKT 9a.\n\n- **Broken_text type**: Similar to the text type, zooming in does not affect clarity. However, the text layer extracted using PyMuPDF exhibits a one-to-many or many-to-many correspondence with what is visually observed. \n\n![example AKT 8 2015](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18505507%2F05787bbe3e387d112d2845c6df35a3f0%2F4.png?generation=1774319930773772&alt=media)\n\n\nThis category includes: AKT 8 2015 and AKT 7a.\n\nFor the broken_text type, we will directly treat them as the image type.\n\nOnce the file type is identified, we pass text-based PDFs directly to the LLM API for processing, where it extracts the corresponding document pairs (which are later split into sentences). The method involves using Regular Expressions (Regex) to identify heading blocks, then passing both the headings and their corresponding text blocks to a LLM api for analysis and the extraction of document pairs (including error correction).\n\nFor image-based PDFs, our workflow is as follows: \nwe first utilize the [open-source GLM-OCR method](https://www.kaggle.com/code/angantyr/text-extraction-from-pdf-docs-using-glm-oc), featuring custom parameter tuning and parallelization enhancements. Then we can get this:\n```python\n{\n    \"p-1_f-0\": \"1. kt 94/k 1263\",\n    \"p-1_f-1\": \"14 1/2 GÍN KB \"KI\" I-lí-dan\",\n    \"p-1_f-2\": \"14 1/2 shekels of silver is owed by Ili-dan\",\n    \"p-1_f-3\": \"DUMU Šu-ki-tim 2 ma-na 10 GÍN / KU\",\n    \"p-1_f-4\": \"son of Sukkutum. 2 minas 10 shekels of\",\n    ...\n}\n```\n|    |   page_num |   field_num |    x |   y |    w |    h |   x_pad |   y_pad |   w_pad |   h_pad |\n|---:|-----------:|------------:|-----:|----:|-----:|-----:|--------:|--------:|--------:|--------:|\n|  0 |          1 |          -1 |    0 |   0 | 2075 | 2809 |       0 |       0 |    2075 |    2809 |\n|  1 |          1 |           0 |  864 | 253 |  378 |   50 |     852 |     241 |     402 |      74 |\n|  2 |          1 |           1 |  166 | 403 |  533 |   48 |     154 |     391 |     557 |      72 |\n|  3 |          1 |           2 | 1167 | 410 |  746 |   52 |    1155 |     398 |     770 |      76 |\n|  4 |          1 |           3 |  162 | 470 |  773 |   49 |     150 |     458 |     797 |      73 |\n|  ... |\n\n<br>\n\nNext, we use Regex to match the headings again. The LLM API then processes these headings and images to generate text. Finally, we run a second pass with the image and text together for fine-grained correction, ensuring a high degree of accuracy.\n\nSome of these PDFs are in Turkish. We're using a combination of LLM APIs with dictionaries & Turkish translation to translate them into English for training.\n\n---\n\n## Train.csv\nRegarding `train.csv`, I have to mention the **OARE** website. For each sample, we visit `https://oare.byu.edu/epigraphies/{oare_id}`. \n\nWe discovered that clicking on a specific word on this page displays its definition. Consequently, we **scraped** the site to obtain the meanings of these words. While not every word has a corresponding definition, the vast majority do, which is more than sufficient for our needs.\n\nAs a result, we obtained a dictionary covering most of the words in train.csv. \n\nHowever, it is important to note that almost all documents in train.csv originate from AKT 5 2008, AKT 6a, AKT 6b, AKT 6c, AKT 6d, AKT 6e, and AKT 8 2015. To improve accuracy, we performed similarity matching between the entries in train.csv and the text extracted from these PDF sources. This allowed us to retrieve the \"ground truth\" or correct form of the documents, as the data quality within the original PDFs is significantly higher. (This involved a lot of manual fixes.)\n\nFollowing these procedures, we used that dictionary to give the LLM a general understanding of what transliteration means, then followed the English sentence breaks to eventually obtain sentence-level parallel corpora. (The alignment in this step might not be perfect, so we have implemented corresponding AUG to address this.)\n\n---\n\n## Published_texts.csv\n\nSimilar to train.csv, almost all samples can be found on the OARE website. However, since translations were unavailable, we used an LLM API to create pseudo-labels for these samples. The process is roughly as follows: we prompt the LLM to translate the original text using the dictionary we retrieved from the OARE site.\n\nexample (The prompt also includes several constraints, such as JSON formatting, mana 60 conversion, ...):\n```\nkey_prompt = \"\"\"\nKIŠIB a-šùr-ma-lik DUMU i-na-a a-na a-šùr-na-da DUMU <gap> ù en-um-a-šùr DUMU <gap> a-pu-tum a-na a-wa-at ṭup-pì-im iḫ-da <gap> ša ú-<gap> ḫa-sí-sú <gap>\n\n{KIŠIB|form:Noun|1. seal, cylinder seal 2. seal impression produced by a cylinder seal 3. sealed clay tablet (legal or administrative document, also letter)}\n{a-šùr-ma-lik|form:Noun|1. Aššur is counselor}\n{DUMU|form:Noun|1. son}\n{i-na-a|form:Noun|nan}\n{a-na|form:Preposition|1. to, for, up to,  toward, against, upon}\n{a-šùr-na-da|form:Noun|1. Praise Aššur!}\n{ù|form:Conjunction|1. and}\n{en-um-a-šùr|form:Noun|1. Mercy O Aššur}\n{a-pu-tum|form:Interjection|1. urgent!, please!}\n{a-wa-at|form:Noun|1. word, matter, lawsuit 2. pl. awâtum - stipulation, rule}\n{ṭup-pì-im|form:Noun|1. (inscribed) tablet (of clay, rarely of other materials) 2. board, flat surface}\n{iḫ-da|form:Finite verb|1. to attend, to watch, to pay attention, to do something carefully 2. to draw someone's attention to something, to alert, to ask someone to pay attention, to take care}\n{ša|form:Pronoun|1. relative pronoun}\n\"\"\"\n```\n\n---\n\n## Publications.csv\n\nFor this file, we provide a fixed number of pages to the LLM API to extract sentences and other elements. While this yields a significant amount of data, it is difficult to achieve document-level alignment.\n\n---\n\nWith that, we have obtained a parallel corpus dataset; the remaining task is now focused on data cleaning.\n\n# 2. Data Processing\n\nActually, the organizers updated the data several times. The `train.csv` I used in Step 1 to match against the PDF data was the second version, which was much better normalized.\n\nFor preprocessing, here’s the main stuff (mainly for pdf-data or LLM results, The second version of train.csv is already quite clean.):\n\n**General Foundation Processing (Applied to Both Sides)**\n- Character-level normalization: Apply Unicode NFKC normalization, remove zero-width characters, and unify the formatting of dashes and hyphens.\n- Special letter simplification: Convert special characters with diacritics (e.g., Ḫ, ḫ) to standard letters (e.g., H, h).\n- Removal of academic editorial symbols: Eliminate various special editorial brackets commonly found in the literature (e.g., ⸢ ⸣ ⌈ ⌉ ⟦ ⟧ [ ], etc.) and footnote superscript numbers.\n- Fraction and decimal formatting: Intelligently convert decimals, mixed numbers (e.g., 1 1/2), or regular fractions in the text into compact Unicode single-character fractions (e.g., ½, ⅓, ¾).\n\n---\n\n**Transliteration Text Side**\nThe primary objective of the source text side is to eliminate typographical variations within the sign system and to standardize determinatives for divine names, toponyms, and similar entities.\n- Determinative and script normalization:\n  - Convert subscript numbers (e.g., ₀₁₂) to standard digits.\n  - Unify superscript determinatives (e.g., ᵏⁱ, ᵈUTU) or superscripts indicated by carets (e.g., ^{ki}) into standard curly brace syntax (e.g., {ki}, {d}UTU).\n  - Intelligently identify and merge determinatives that are separated from proper nouns (e.g., d UTU -> {d}UTU).\n- Sign composition and placeholder cleanup:\n  - Convert multiplication signs (×, ✖) representing sign compounding into periods (.).\n  - Clean up or convert the unknown sign marker \"x\" attached to the end of a word or before a parenthesis (e.g., SUDx -> SUD).\n- Removal of specific academic annotations: Remove specifically formatted parenthetical annotations (e.g., editorial notes like \"(over erased)\", \"(met.)\"), standalone line numbers (e.g., \"10.\"), and the pipe character (|) unique to the Oracc system.\n- Gap handling and alignment: Unify and normalize various symbols indicating missing text (e.g., consecutive periods \"...\", the standalone letter \"x\", \"missing\", etc.) into a single placeholder `<gap>`, and strictly clean up extraneous spaces and punctuation before and after the `<gap>` marker.\n- Dictionary-level hard replacement: Erase punctuation marks such as question marks and exclamation points based on the `transliteration_map`, and correct specific spellings of personal names and anomalous annotations.\n\n---\n\n**Translation Side**\nThe primary objective of the translation side is to enhance the natural linguistic fluency of the text, eliminating alternative options and academic jargon left by translators.\n- Aggressive annotation and markup clearance: Compared to the source text side, the translation side indiscriminately removes all translational explanations within parentheses, while also eliminating asterisks (*) and quotation marks (\") used for emphasis.\n- Academic/scribal terminology removal: Automatically identify and delete academic annotations such as \"fem. plur.\" (feminine plural), \"lit.\" (literally), \"sic\" (thus in the original), \"i.e.\", and similar terms.\n- Intelligent decision-making for slash alternatives:\n  - When an \"either/or\" scenario appears in the translation (e.g., a / the, like / instead, promised / been), the script automatically selects the vocabulary that better fits the context or predefined settings.\n  - Automatically handle capitalization inheritance (if the original word is capitalized, the selected word will automatically be capitalized).\n- Roman numeral month conversion: Identify expressions such as \"Month XII\" and convert them to Arabic numerals, such as \"Month 12\".\n- Expansion of complex measurement units: Based on the `translation_map`, expand complex fractional weights unique to antiquity (e.g., 12 5/12 shekels) into more readable text (e.g., 12 ⅓ shekels 15 grains).\n- Punctuation and typographical correction: Fix extraneous spaces before punctuation marks (e.g., commas, semicolons) and ensure compactness between numbers and fractions.\n- Gap mapping: Similarly, replace \"x\" placeholders or ellipses appearing in the translation with `<gap>` to ensure strict alignment with the source text side in subsequent machine learning or text comparison processes.\n\n---\n\nActually, this part skips over a considerable amount of manual effort—things like manually checking for anomalies, using regex for matching, and then incorporating those into the cleaning rules, this is our final clean func:\n\n[clean function](https://www.kaggle.com/code/wscmisx/dpc-clean-function/notebook?scriptVersionId=306066643)\n\n# 3. Training\n- **Dynamic Context Sampling**\n\nThis technique aims to address two primary issues:\n\n1. Potential sentence misalignments during the extraction phase; we intend to make the model less sensitive to sentence alignment.\n2. The unknown sentence length distribution of the test set, which may differ from our segmented data; we utilize this approach to enable the model to adapt to varying input and output lengths.\n\nSpecifically, we implemented the following mechanisms:\n\n- **Zipf / Geometric Mixture Distribution Sampling (K)**: The number of contextual sentences included is dynamically determined for each instance . Following Zipf's law (long-tail distribution), the model predominantly encounters short contexts and occasionally processes extremely long contexts, thereby enhancing its adaptability to varying text lengths.\n- **Symmetric / Asymmetric Allocation (Symmetric LR)**: The ratio of left to right contextual sentences (L and R) is dynamically determined.\n- **Random Contextual Sentence Insertion (Random Context)**: There is a 20% probability (`random_ctx_prob = 0.20`) of bypassing contiguous context and instead randomly sampling and concatenating sentences from the same article. This compels the model to learn not only the local context but also the global topic, preventing the rote memorization of absolute sentence orders.\n```\ndef _build_k_cdf(self):\n    \"\"\"\n    Build a CDF over K in [1, k_max].\n    - zipf:     P(K) ∝ 1/K  (heavy-tail, good length coverage)\n    - geom_mix: (1-α)·Geom(p) + α·Zipf(1/K)\n    \"\"\"\n    keys = list(range(1, self.k_max + 1))\n\n    if self.k_sampling == \"geom_mix\":\n        # geometric over {1..k_max}: p*(1-p)^(k-1)\n        geom, zipf = [self.geom_p * ((1.0 - self.geom_p) ** (k - 1)) for k in keys], [1.0 / k for k in keys]\n        probs = [(1.0 - self.long_tail_mix) * g + self.long_tail_mix * z for g, z in zip(geom, zipf)]\n    else:\n        # default: Zipf / 1-K\n        probs = [1.0 / k for k in keys]\n\n    s = sum(probs); probs = [p / s for p in probs]\n    cdf = list(accumulate(probs))\n    cdf[-1] = 1.0\n    return keys, cdf\n\ndef _sample_K(self):\n    r = random.random()\n    i = bisect_left(self._k_cdf, r)\n    if i >= len(self._k_keys): i = len(self._k_keys) - 1\n    return int(self._k_keys[i])\n\n@staticmethod\ndef _sample_LR(K: int):\n    # L ~ Binomial(K-1, 0.5), R = K-1-L\n    if K <= 1: return 0, 0\n    L = int(np.random.binomial(K - 1, 0.5))\n    R = int((K - 1) - L)\n    return L, R\n```\n- **R-Drop**\n\nFor a given input x, it is passed through the network twice (due to Dropout, the two forward propagation results, y_1 and y_2, will exhibit minor variances). In addition to calculating the standard cross-entropy loss, the symmetric Kullback-Leibler (KL) divergence between these two output distributions (logits) is computed. This enforces output consistency in the presence of internal noise (Dropout), significantly mitigating overfitting.\n\n- **PGD - Projected Gradient Descent**\n\nThis involves identifying the \"worst-case perturbations\" within the continuous embedding space and injecting them into the input, ensuring that the model can still produce accurate predictions even under slight adversarial perturbations.\n\n- **EMA**\n\nExponential Moving Average, a commonly adopted technique in deep learning competitions to improve model generalization and stability.\n\n- **Engineering Optimizations**\n\nIncorporating these complex components necessitates engineering optimizations, which include:\n\n- DeepSpeed ZeRO-2\n- Gradient Checkpointing\n- Custom Trainer implementation\n\nFinal parameters:\n```python\nk_max=10\nk_sampling=\"zipf\"\ngeom_p=0.35\nrandom_ctx_prob=0.20\nRDROP_ALPHA = 0.8\nPGD_ENABLE = True\nPGD_STEPS  = 3\nPGD_EPS    = 0.5\nPGD_ALPHA  = 0.2\nPGD_EMB_KEYWORDS = (\"shared\", \"embed_tokens\", \"embeddings\")\n```\n\n\n# 4. Experiment\nThis is our primary experimental pipeline (the optimal path for single-mode optimization).\nBy using different subsets of data, we trained a variety of models. Generally, it works like this:\n\n| Model | Method | LB | PB |\n| :--- | :--- | :--- | :--- |\n| byt5-large | v1 No external data (only aligned and cleaned `train.csv`) | 34.0 | 34.3 |\n| byt5-large | v2 Added Turkish-to-English data | 36.9 | 37.0 |\n| byt5-large | v3 Added high quality publications data | 38.7 | 38.9 |\n| byt5-large | v4 Pre-trained for 5 epochs using all `publications` data before formal training (filtered by quality) | 38.6 | 39.5 |\n| byt5-large | v5 Pre-trained for 5 epochs using all `publications` data and `published_texts` pseudo-labels before formal training (filtered by quality) | 38.9 | 39.3 |\n| byt5-base | v6 Formally trained using `publications` data, `published_texts` pseudo-labels, alongside main data | 39.3 | 39.9 |\n\n<br>\n\nInitially, we thought the extra data was too noisy and left it out of the formal training phase, but it actually performed better once we included it—probably just because of the large volume.\n\n# 5. Inference\n\nOur final submission consists of 15 models, primarily detailed as follows:\n\n| Model Description | LB | PB |\n| :--- | :--- | :--- |\n| Large model trained on data subset v3 | 38.7 | 38.9 |\n| Large model trained on data subset v3 with name augmentation | 37.8 | 38.8 |\n| XL model trained on data subset v3 | 37.9 | 38.3 |\n| Large model trained on data subset v4 | 38.6 | 39.5 |\n| Large model trained on data subset v5 | 38.9 | 39.3 |\n| Base model trained on data subset v5 | 38.8 | 39.0 |\n| Base model trained on data subset v6 | 39.2 | 39.5 |\n| Base model trained on data subset v6 with additional filtering and cleaning (v1) | 39.3 | 39.9 |\n| Base model trained on data subset v6 with additional filtering and cleaning (v2) | 38.3 | 39.2 |\n| Base model trained on data subset v6 with additional filtering and cleaning (v3) | 38.2 | 39.5 |\n| Base model trained on data subset v6 with additional filtering and cleaning (v4) | 38.4 | 39.6 |\n\nWe performed 5-fold cross-validation training based on our best-performing model on the Public Leaderboard (LB score 39.3: Base model trained on data subset v6 with additional filtering and cleaning (v1)). Although these models were not submitted individually, they were also incorporated into the MBR (Minimum Bayes Risk) ensemble.\n\nTo accelerate inference, we restricted the output to four results per model using a beam size of 4 (`beam=4`), without applying any sampling methods (our experiments indicated that utilizing sampling within the MBR process leads to a decrease in score). \n\nThe models were grouped and deployed across two T4 GPUs for execution.\n\n![15models](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F18505507%2F2d32d5e4df1b2bbafc1e969535c8f85f%2Fall_models.png?generation=1774331390404829&alt=media)\n\n[**Final Score:** 40.7  |  **Execution Time:** 208m 50s](https://www.kaggle.com/code/wscmisx/final-dpc-ensemble-mbr-infer)\n\n\n# 6. What didn't work\n\n- **Organizing the current target sentence and its context like this (during both training and inference):**\n```text\ntranslate Akkadian to English. Translate TARGET only. Use CONTEXT only as reference.\nTARGET:\nki-ma mup-pì-ni ta-áa-me-a-ni a-ma-kam lu a-na aí-mì-im a-na É.GAL-lim i-dí-in lu té-ra-at É.GAL-lim ú-kà-lim lu na-aí-ma a-dí-ni lá i-dí-in ma-lá KÙ.AN na-áa-ú ni-bi„-it a-aí-im au-um-au ú au-mì a-bi„-au i-na mup-pì-im lu-up-ta-nim-ma ia-tí aí-ip-ri-ni aé-bi„-lá-nim\n\nCONTEXT BEFORE:\num-ma kà-ru-um kà-ni-ia-ma a-na aa-qí-il… da-tim aí-ip-ri-ni kà-ar kà-ar-ma ú wa-bar-ra-tim qí-bi„-ma mup-pu-um aa a-lim(ki) i-li-kam i-na mup-pì-im aa a-lim(ki) ia-tù u„-mì-im a-nim ma-ma-an KÙ.AN i-aa-ú-mu-ni i-na né-mì-lim da-aùr ú-lá e-WA ia-ra-tí-au kà-ru-um kà-ni-ia i-lá-qé\n\nCONTEXT AFTER:\nme-+e-er mup-pì-ni a-na kà-ar kà-ar-ma ú wa-bar-ra-tim aé-bi„-lá KÙ.AN lu a-na DAM.GÀR-ru-tim i-dí-in au-mì a-wi-lim lu-up-ta-nim\n```\n\n- **Data augmentation on names and dictionaries in the source text during training**\nRandomly altering the capitalization of names, and using a dictionary to randomly select a `norm` form for many-to-one `norm` -> `lexeme` mappings. \nThe single-model score didn't improve much, but we still included these in our ensemble.\n\n- **Weight fusion / averaging**\nThis showed almost no improvement and even resulted in lower scores than the original single model. After a few attempts, we stopped trying this and switched to using MBR (Minimum Bayes Risk) for ensembling instead.\n\n- **Setting a very long `maxlen` and training at the document level**\nUnsurprisingly, the results were poor because the test set is evaluated at the sentence level.\n\n- **Using Large Language Models (Qwen, LLaMA, etc.)**\nThis was likely due to the tokenizer or the fact that my data wasn't clean enough in the early stages of the competition. Without applying any special tricks, the performance of a trained 14B model was only average. However, I was very glad to see top-ranking participants successfully taking on the challenge using LLMs—an excellent solution!\n\n- **Bidirectional training (adding `akk->eng`)**\nThere wasn't much progress in terms of single-model improvement. We didn't have extra submission quotas to test this before ensembling, but perhaps including it in the ensemble could have provided a slight boost to the final results.",
    "3427499": "Congratulations! The thoroughness and patience you demonstrated in data processing and augmentation are truly amazing ! I’ve learned so much from it.",
    "3427513": "True, this is actually just the tip of the iceberg. The countless hours of manual work, trial and error, and constant tweaking my teammates and I went through didn't even make it into this writeup.",
    "3427514": "Congratulations! Excellent solution sharing.",
    "3427520": "Thank you!",
    "3427532": "I'm glad you found the OCR solution useful!\n\nKudos on the great job of testing so many options and getting a 6th place gold!",
    "3427533": "Your code has been a huge help to us! We’re using LLMs to refine the details after the initial extraction.",
    "3427595": "Congratulations to your team on winning the gold medal. The amount of engineering work is truly astonishing, and congratulations as well on your promotion to Master!",
    "3427597": "Thank you! I hope you become a master soon, too!",
    "3427675": "Brilliant work！What a fucking feat for 3G！",
    "3427988": "Congrats @wscmisx !!",
    "3428078": "Thank you so much bro🥰",
    "3430291": "Wow, Thats a genius thinking. Well deserved brother. Congratulations 🎊"
  },
  "source": "meta"
}