Token Classification
Transformers
Safetensors
Ancient Greek (to 1453)
char_bert_joint
ancient-greek
classical-philology
character-level
part-of-speech-tagging
lemmatization
dependency-parsing
custom_code
Instructions to use Ericu950/CharDiff-grc-tagger-parser with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Ericu950/CharDiff-grc-tagger-parser with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("token-classification", model="Ericu950/CharDiff-grc-tagger-parser", trust_remote_code=True)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("Ericu950/CharDiff-grc-tagger-parser", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
Add ud=True flag to decode(): converts native Perseus/AGDT output to Universal Dependencies (HamleDT-style COORD/AuxP/Pnom restructuring + XPOS->UPOS+FEATS mapping)
Browse files- processing_char_bert_joint.py +236 -4
processing_char_bert_joint.py
CHANGED
|
@@ -4,7 +4,11 @@ Turns one or more already word-tokenized sentences into the model's input tensor
|
|
| 4 |
four character planes as the base CharBertProcessor, plus a `word_id` array pooling characters
|
| 5 |
into words, a `cap` plane -- an actual model input for this fine-tuned checkpoint, unlike the
|
| 6 |
base model where capitalization is output-only -- and a `seg_id` plane), and decodes the
|
| 7 |
-
model's raw logits back into
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
|
| 9 |
Interface: call the processor with `List[str]` (one already-tokenized sentence, e.g.
|
| 10 |
`["ὁ", "ἄνθρωπος", "τρέχει", "."]`) or `List[List[str]]` (a batch of such sentences) --
|
|
@@ -57,6 +61,225 @@ def _pack_dia(acc, br, iota, diaer):
|
|
| 57 |
return ((acc * 3 + br) * 2 + iota) * 2 + diaer
|
| 58 |
|
| 59 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
# ---- edit-script machinery (tagger/edits.py, ported verbatim on strings) ---------------------
|
| 61 |
|
| 62 |
GRAVE, ACUTE = "̀", "́"
|
|
@@ -355,10 +578,19 @@ class CharBertJointProcessor:
|
|
| 355 |
|
| 356 |
# ---------------------------------------------------------------- full decode
|
| 357 |
|
| 358 |
-
def decode(self, model_out, batch, use_lexicon: bool = True) -> list[list[dict]]:
|
| 359 |
"""model_out: a CharBertJointOutput (or the equivalent return_dict=False tuple).
|
| 360 |
batch: the dict returned by __call__ (needs `_forms`).
|
| 361 |
-
-> per sentence, a list of
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 362 |
word_mask = model_out.word_mask if hasattr(model_out, "word_mask") else model_out[-1]
|
| 363 |
xpos_logits = model_out.xpos_logits if hasattr(model_out, "xpos_logits") else model_out[0]
|
| 364 |
script_logits = model_out.script_logits if hasattr(model_out, "script_logits") else model_out[1]
|
|
@@ -390,5 +622,5 @@ class CharBertJointProcessor:
|
|
| 390 |
if labels_out is not None and self.deprel_vocab else None)
|
| 391 |
sent_out.append(dict(form=form, xpos=xpos, lemma=lemma, upos=upos,
|
| 392 |
head=head, deprel=deprel))
|
| 393 |
-
results.append(sent_out)
|
| 394 |
return results
|
|
|
|
| 4 |
four character planes as the base CharBertProcessor, plus a `word_id` array pooling characters
|
| 5 |
into words, a `cap` plane -- an actual model input for this fine-tuned checkpoint, unlike the
|
| 6 |
base model where capitalization is output-only -- and a `seg_id` plane), and decodes the
|
| 7 |
+
model's raw logits back into predictions. The model's NATIVE tag/relation inventory is the
|
| 8 |
+
Perseus/AGDT scheme (9-position morphological tags; Prague-style dependency relations like
|
| 9 |
+
SBJ/OBJ/ATR/COORD/AuxP -- the scheme its OGA training treebank uses), not Universal
|
| 10 |
+
Dependencies. Pass `ud=True` to `decode()` for Universal Dependencies-style output instead
|
| 11 |
+
(UPOS/FEATS/deprel) -- see `to_ud()` below for the conversion and its scope/limitations.
|
| 12 |
|
| 13 |
Interface: call the processor with `List[str]` (one already-tokenized sentence, e.g.
|
| 14 |
`["ὁ", "ἄνθρωπος", "τρέχει", "."]`) or `List[List[str]]` (a batch of such sentences) --
|
|
|
|
| 61 |
return ((acc * 3 + br) * 2 + iota) * 2 + diaer
|
| 62 |
|
| 63 |
|
| 64 |
+
# ==== Universal Dependencies conversion ========================================================
|
| 65 |
+
#
|
| 66 |
+
# The model's native output is the Perseus/AGDT annotation scheme its OGA training treebank
|
| 67 |
+
# uses: a 9-position morphological tag (pos.person.number.tense.mood.voice.gender.case.degree,
|
| 68 |
+
# one letter per position, "-" for n/a) and Prague-style dependency relations (SBJ, OBJ, ATR,
|
| 69 |
+
# ADV, PNOM, PRED, APOS, COORD, Aux*, ATV/AtvV, ExD, MWE, OCOMP) -- NOT Universal Dependencies.
|
| 70 |
+
#
|
| 71 |
+
# This is a solved, documented conversion problem, not something designed from scratch here:
|
| 72 |
+
# both UD_Ancient_Greek-Perseus and UD_Ancient_Greek-PROIEL are themselves automatic
|
| 73 |
+
# conversions of exactly this annotation style, via Zeman & Ramasamy's HamleDT pipeline
|
| 74 |
+
# (Zeman et al. 2014, "HamleDT: Harmonized Multi-Language Dependency Treebank", LRE) --
|
| 75 |
+
# specifically Treex::Block::HamleDT::{GRC::Harmonize,HarmonizePerseus,Udep} and
|
| 76 |
+
# Treex::Tool::PhraseBuilder::PragueToUD (github.com/ufal/treex), plus the morphology decoder
|
| 77 |
+
# Lingua::Interset::Tagset::GRC::Conll (github.com/dan-zeman/interset). The tables and
|
| 78 |
+
# restructuring rules below port that design to our exact tag/relation inventory.
|
| 79 |
+
#
|
| 80 |
+
# Two relations require real TREE restructuring, not just a label rename, because UD attaches
|
| 81 |
+
# function words/conjuncts differently than Prague style:
|
| 82 |
+
# - COORD: Prague style makes the coordinating conjunction the head, conjuncts its children.
|
| 83 |
+
# UD makes the FIRST conjunct the head, the conjunction becomes a `cc` dependent of it, and
|
| 84 |
+
# other conjuncts become `conj` dependents.
|
| 85 |
+
# - AuxP: Prague style makes the preposition the head of its complement. UD reverses this:
|
| 86 |
+
# the nominal complement becomes the head, the preposition becomes a `case` dependent of it.
|
| 87 |
+
# - Pnom (predicate nominal / copula): Prague style makes the copula verb the head with the
|
| 88 |
+
# nominal attached as Pnom. UD makes the NOMINAL the head, the copula a `cop` dependent,
|
| 89 |
+
# and reattaches the subject to the nominal instead of the verb.
|
| 90 |
+
#
|
| 91 |
+
# What relation does the PROMOTED node (first conjunct / nominal complement) get in UD, given
|
| 92 |
+
# it used to be a plain dependent of COORD/AuxP? In full AGDT/HamleDT, the coordinator's or
|
| 93 |
+
# preposition's own outbound relation carries the phrase's true external function via a
|
| 94 |
+
# _CO/_AP label suffix (e.g. "Sb_Co"), which HamleDT strips off and hands to the promoted node.
|
| 95 |
+
# Our 24-label inventory has no such suffix mechanism -- verified directly against real training
|
| 96 |
+
# examples in oga_sota.conllu (e.g. "ἀπὸ" and "ἕως" each attach to their external governor with
|
| 97 |
+
# deprel=AuxP verbatim, and "καὶ" attaches with deprel=COORD verbatim, regardless of whether the
|
| 98 |
+
# phrase functions as a locative adjunct, a coordinated subject, or anything else). So the label
|
| 99 |
+
# itself never carries a recoverable function here, and the promoted node gets UD's generic
|
| 100 |
+
# fallback instead of a falsely precise guess: "obl" for AuxP nominals, "dep" for COORD first
|
| 101 |
+
# conjuncts (see Stage 1/2 below).
|
| 102 |
+
#
|
| 103 |
+
# SCOPE: this ports the core, well-documented HamleDT/UD restructuring rules and a straight
|
| 104 |
+
# per-label relabeling for everything else. It does NOT replicate every Ancient-Greek-specific
|
| 105 |
+
# refinement in the full pipeline (e.g. GRC::Harmonize's regex-based negation-particle
|
| 106 |
+
# splitting, or the is_member/shared-modifier _CO/_AP distinction discussed above) -- treat this
|
| 107 |
+
# as a faithful, reasonably-scoped port, not a byte-exact reproduction of the official
|
| 108 |
+
# UD_Ancient_Greek-Perseus release.
|
| 109 |
+
|
| 110 |
+
# position 1 (POS) -> UD UPOS. 'c' defaults to CCONJ, refined to SCONJ if its converted deprel
|
| 111 |
+
# is "mark" (i.e. it was AuxC); 'v' stays VERB regardless of mood (participles etc. get
|
| 112 |
+
# VerbForm=Part as a feature, not a UPOS change, matching standard UD_Ancient_Greek practice).
|
| 113 |
+
_POS_TO_UPOS = {
|
| 114 |
+
"a": "ADJ", "c": "CCONJ", "d": "ADV", "g": "PART", "i": "INTJ", "l": "DET",
|
| 115 |
+
"m": "NUM", "n": "NOUN", "p": "PRON", "r": "ADP", "u": "PUNCT", "v": "VERB", "x": "X",
|
| 116 |
+
}
|
| 117 |
+
_NUMBER = {"d": "Dual", "p": "Plur", "s": "Sing"}
|
| 118 |
+
_TENSE_ASPECT = { # (Tense, Aspect-or-None); aorist/perfect/future-perfect analysis per
|
| 119 |
+
"a": ("Past", "Perf"), # aorist = perfective past
|
| 120 |
+
"f": ("Fut", None),
|
| 121 |
+
"i": ("Past", "Imp"), # imperfect
|
| 122 |
+
"l": ("Pqp", None), # pluperfect
|
| 123 |
+
"p": ("Pres", None),
|
| 124 |
+
"r": ("Pres", "Perf"), # perfect = present relevance of a completed action
|
| 125 |
+
"t": ("Fut", "Perf"), # future perfect
|
| 126 |
+
}
|
| 127 |
+
_MOOD_VERBFORM = { # (Mood-or-None, VerbForm)
|
| 128 |
+
"i": ("Ind", "Fin"), "s": ("Sub", "Fin"), "o": ("Opt", "Fin"), "m": ("Imp", "Fin"),
|
| 129 |
+
"n": (None, "Inf"), "p": (None, "Part"), "g": (None, "Gdv"),
|
| 130 |
+
}
|
| 131 |
+
_VOICE = {"a": "Act", "m": "Mid", "p": "Pass", "e": "Mid"} # e = mediopassive, approximated as Mid
|
| 132 |
+
_GENDER = {"c": "Com", "f": "Fem", "m": "Masc", "n": "Neut"}
|
| 133 |
+
_CASE = {"a": "Acc", "d": "Dat", "g": "Gen", "n": "Nom", "v": "Voc"}
|
| 134 |
+
_DEGREE = {"c": "Cmp", "s": "Sup"}
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
def xpos_to_upos_feats(xpos: str) -> tuple[str, dict]:
|
| 138 |
+
"""9-position Perseus/AGDT tag (e.g. "v3spia---") -> (UD UPOS, UD FEATS dict).
|
| 139 |
+
"-" in any position means not applicable/unspecified and is simply omitted from FEATS."""
|
| 140 |
+
pos = xpos[0] if xpos else "x"
|
| 141 |
+
upos = _POS_TO_UPOS.get(pos, "X")
|
| 142 |
+
feats = {}
|
| 143 |
+
rest = xpos[1:].ljust(8, "-")
|
| 144 |
+
person, number, tense, mood, voice, gender, case, degree = rest[:8]
|
| 145 |
+
if person in "123":
|
| 146 |
+
feats["Person"] = person
|
| 147 |
+
if number in _NUMBER:
|
| 148 |
+
feats["Number"] = _NUMBER[number]
|
| 149 |
+
if tense in _TENSE_ASPECT:
|
| 150 |
+
t, a = _TENSE_ASPECT[tense]
|
| 151 |
+
feats["Tense"] = t
|
| 152 |
+
if a:
|
| 153 |
+
feats["Aspect"] = a
|
| 154 |
+
if mood in _MOOD_VERBFORM:
|
| 155 |
+
m, vf = _MOOD_VERBFORM[mood]
|
| 156 |
+
if m:
|
| 157 |
+
feats["Mood"] = m
|
| 158 |
+
feats["VerbForm"] = vf
|
| 159 |
+
if voice in _VOICE:
|
| 160 |
+
feats["Voice"] = _VOICE[voice]
|
| 161 |
+
if gender in _GENDER:
|
| 162 |
+
feats["Gender"] = _GENDER[gender]
|
| 163 |
+
if case in _CASE:
|
| 164 |
+
feats["Case"] = _CASE[case]
|
| 165 |
+
if degree in _DEGREE:
|
| 166 |
+
feats["Degree"] = _DEGREE[degree]
|
| 167 |
+
return upos, feats
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
# Straight per-label relabeling for everything that ISN'T COORD/AuxP/Pnom (those need the tree
|
| 171 |
+
# restructuring in to_ud() below). Homoglyph artifacts in the training treebank (Greek "Ζ"/"Κ"
|
| 172 |
+
# instead of Latin "Z"/"K" in a couple of AuxZ/AuxK instances) are normalized first.
|
| 173 |
+
_HOMOGLYPH_FIX = {"AuxΖ": "AuxZ", "AuxΚ": "AuxK"}
|
| 174 |
+
_SIMPLE_DEPREL = {
|
| 175 |
+
"SBJ": "nsubj", "OBJ": "obj", "OCOMP": "obj", "APOS": "appos",
|
| 176 |
+
"AuxC": "mark", "AuxV": "aux", "AuxG": "punct", "AuxK": "punct", "AuxX": "punct",
|
| 177 |
+
"AuxR": "expl", "AuxY": "cc", "AuxZ": "advmod", "AuxΖ": "advmod", "AuxΚ": "punct",
|
| 178 |
+
"ATV": "xcomp", "AtvV": "xcomp", "ExD": "orphan", "MWE": "fixed",
|
| 179 |
+
"PRED": "root",
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
def _normalize_deprel(label: str | None) -> str | None:
|
| 184 |
+
return _HOMOGLYPH_FIX.get(label, label)
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
def to_ud(sent: list[dict]) -> list[dict]:
|
| 188 |
+
"""Convert one sentence's native decode() output (list of {form, xpos, lemma, upos, head,
|
| 189 |
+
deprel} dicts, 1-indexed word order, head=0 meaning root) into UD-style output: each dict
|
| 190 |
+
becomes {form, lemma, upos (UD), xpos (native tag, kept as-is per UD convention that XPOS
|
| 191 |
+
is language-specific and optional), feats (dict), head, deprel (UD)}.
|
| 192 |
+
|
| 193 |
+
See the module-level comment above for the restructuring rules and scope/limitations.
|
| 194 |
+
"""
|
| 195 |
+
n = len(sent)
|
| 196 |
+
if n == 0:
|
| 197 |
+
return []
|
| 198 |
+
heads = [0] * (n + 1)
|
| 199 |
+
deprels = [None] * (n + 1)
|
| 200 |
+
native_upos = [None] * (n + 1)
|
| 201 |
+
for i, w in enumerate(sent, start=1):
|
| 202 |
+
heads[i] = w["head"] if w["head"] is not None else 0
|
| 203 |
+
deprels[i] = _normalize_deprel(w["deprel"])
|
| 204 |
+
native_upos[i] = w["upos"]
|
| 205 |
+
|
| 206 |
+
def children_of():
|
| 207 |
+
c = {i: [] for i in range(0, n + 1)}
|
| 208 |
+
for i in range(1, n + 1):
|
| 209 |
+
c.setdefault(heads[i], []).append(i)
|
| 210 |
+
return c
|
| 211 |
+
|
| 212 |
+
# Stage 1: COORD -> first-conjunct-head restructuring.
|
| 213 |
+
for c in [i for i in range(1, n + 1) if deprels[i] == "COORD"]:
|
| 214 |
+
kids = sorted(children_of().get(c, []))
|
| 215 |
+
if not kids:
|
| 216 |
+
continue
|
| 217 |
+
first, rest = kids[0], kids[1:]
|
| 218 |
+
heads[first] = heads[c]
|
| 219 |
+
deprels[first] = "dep" # COORD's own outbound label carries no recoverable function; see above
|
| 220 |
+
heads[c] = first
|
| 221 |
+
deprels[c] = "cc"
|
| 222 |
+
for k in rest:
|
| 223 |
+
heads[k] = first
|
| 224 |
+
if deprels[k] not in ("AuxG", "AuxK", "AuxX", "AuxΖ", "AuxΚ"): # not a punctuation child
|
| 225 |
+
deprels[k] = "conj"
|
| 226 |
+
# punctuation children just reattach to `first`, keeping their own (later-relabeled) deprel
|
| 227 |
+
|
| 228 |
+
# Stage 2: AuxP -> case restructuring (nominal complement becomes the head).
|
| 229 |
+
for p in [i for i in range(1, n + 1) if deprels[i] == "AuxP"]:
|
| 230 |
+
kids = sorted(children_of().get(p, []))
|
| 231 |
+
if not kids:
|
| 232 |
+
continue
|
| 233 |
+
nominal, others = kids[0], kids[1:]
|
| 234 |
+
heads[nominal] = heads[p]
|
| 235 |
+
deprels[nominal] = "obl" # AuxP's own outbound label carries no recoverable function; see above
|
| 236 |
+
heads[p] = nominal
|
| 237 |
+
deprels[p] = "case"
|
| 238 |
+
for o in others:
|
| 239 |
+
heads[o] = nominal
|
| 240 |
+
|
| 241 |
+
# Stage 3: Pnom (predicate nominal) -> copula reversal. The nominal becomes the head in
|
| 242 |
+
# place of the copula verb; the verb becomes `cop`; anything else attached to the verb
|
| 243 |
+
# (chiefly the subject) is reattached to the nominal.
|
| 244 |
+
for nom in [i for i in range(1, n + 1) if deprels[i] == "PNOM"]:
|
| 245 |
+
verb = heads[nom]
|
| 246 |
+
if verb == 0:
|
| 247 |
+
continue
|
| 248 |
+
heads[nom] = heads[verb]
|
| 249 |
+
deprels[nom] = deprels[verb] if deprels[verb] != "PRED" else "root"
|
| 250 |
+
heads[verb] = nom
|
| 251 |
+
deprels[verb] = "cop"
|
| 252 |
+
for i in range(1, n + 1):
|
| 253 |
+
if i != nom and heads[i] == verb:
|
| 254 |
+
heads[i] = nom
|
| 255 |
+
|
| 256 |
+
# Stage 4: everything else -- straight relabel, with a few UPOS/context-sensitive refinements.
|
| 257 |
+
for i in range(1, n + 1):
|
| 258 |
+
d = deprels[i]
|
| 259 |
+
if d in ("cc", "conj", "case", "cop", "dep", "obl"): # already converted above
|
| 260 |
+
continue
|
| 261 |
+
if d == "ATR":
|
| 262 |
+
deprels[i] = ("det" if native_upos[i] == "l"
|
| 263 |
+
else "amod" if native_upos[i] == "a"
|
| 264 |
+
else "nmod")
|
| 265 |
+
elif d == "ADV":
|
| 266 |
+
deprels[i] = ("advmod" if native_upos[i] == "d"
|
| 267 |
+
else "advcl" if native_upos[i] == "v" else "obl")
|
| 268 |
+
elif d in _SIMPLE_DEPREL:
|
| 269 |
+
deprels[i] = _SIMPLE_DEPREL[d]
|
| 270 |
+
# else: already UD-style (e.g. a second pass over an already-converted label) or
|
| 271 |
+
# unrecognized -- left as-is rather than silently dropped.
|
| 272 |
+
|
| 273 |
+
out = []
|
| 274 |
+
for i, w in enumerate(sent, start=1):
|
| 275 |
+
upos, feats = xpos_to_upos_feats(w["xpos"])
|
| 276 |
+
if deprels[i] == "mark" and upos == "CCONJ":
|
| 277 |
+
upos = "SCONJ" # refine c (CCONJ default) once we know it introduced a subordinate clause
|
| 278 |
+
out.append(dict(form=w["form"], lemma=w["lemma"], upos=upos, xpos=w["xpos"],
|
| 279 |
+
feats=feats, head=heads[i], deprel=deprels[i]))
|
| 280 |
+
return out
|
| 281 |
+
|
| 282 |
+
|
| 283 |
# ---- edit-script machinery (tagger/edits.py, ported verbatim on strings) ---------------------
|
| 284 |
|
| 285 |
GRAVE, ACUTE = "̀", "́"
|
|
|
|
| 578 |
|
| 579 |
# ---------------------------------------------------------------- full decode
|
| 580 |
|
| 581 |
+
def decode(self, model_out, batch, use_lexicon: bool = True, ud: bool = False) -> list[list[dict]]:
|
| 582 |
"""model_out: a CharBertJointOutput (or the equivalent return_dict=False tuple).
|
| 583 |
batch: the dict returned by __call__ (needs `_forms`).
|
| 584 |
+
-> per sentence, a list of dicts, word order.
|
| 585 |
+
|
| 586 |
+
Default (ud=False): the model's NATIVE Perseus/AGDT-style output --
|
| 587 |
+
{form, xpos, lemma, upos, head, deprel}, where `upos` is the Perseus single-letter
|
| 588 |
+
code (not UD) and `deprel` is a Prague-style relation (SBJ/OBJ/ATR/COORD/AuxP/...).
|
| 589 |
+
|
| 590 |
+
ud=True: Universal Dependencies-style output instead -- {form, lemma, upos (UD),
|
| 591 |
+
xpos (native tag, kept as-is per UD convention), feats (dict), head, deprel (UD)}.
|
| 592 |
+
See `to_ud()` for the conversion (a documented, but not 100%-official-treebank-exact,
|
| 593 |
+
port of the HamleDT/UD_Ancient_Greek-Perseus conversion methodology)."""
|
| 594 |
word_mask = model_out.word_mask if hasattr(model_out, "word_mask") else model_out[-1]
|
| 595 |
xpos_logits = model_out.xpos_logits if hasattr(model_out, "xpos_logits") else model_out[0]
|
| 596 |
script_logits = model_out.script_logits if hasattr(model_out, "script_logits") else model_out[1]
|
|
|
|
| 622 |
if labels_out is not None and self.deprel_vocab else None)
|
| 623 |
sent_out.append(dict(form=form, xpos=xpos, lemma=lemma, upos=upos,
|
| 624 |
head=head, deprel=deprel))
|
| 625 |
+
results.append(to_ud(sent_out) if ud else sent_out)
|
| 626 |
return results
|