Download tokenization_ourmorph.py from juand-r/morpheus-10M-v2: direct link, hf CLI and curl.
- Browser
- Download file 6.01 kB
-
https://huggingface.co/juand-r/morpheus-10M-v2/resolve/main/tokenization_ourmorph.py
- Command line
-
hf download hf://juand-r/morpheus-10M-v2/tokenization_ourmorph.py
-
curl -L -o tokenization_ourmorph.py https://huggingface.co/juand-r/morpheus-10M-v2/resolve/main/tokenization_ourmorph.py
6.01 kB
| #!/usr/bin/env python3 | |
| """HuggingFace slow-tokenizer wrapper for our morphological tokenizer, for the BabyLM leaderboard submission. | |
| The official strict harness (`sentence_zero_shot/dataset.py`) calls `tokenizer(text, return_offsets_mapping=True)` | |
| and locates the scored completion by CHARACTER offsets. A pure-Python tokenizer normally can't return offsets; | |
| this subclass overrides the encode path to emit word-granularity `offset_mapping` (via OurTokenizer.encode_with_spans), | |
| which is sufficient because completions always begin at a whitespace boundary. | |
| Ship this file + our_vocab.json (+ the analyzer resources) with the model and load via `trust_remote_code=True`. | |
| Special tokens are NOT auto-added (add_special_tokens is a no-op) to match the harness's no-BOS default for | |
| from-scratch tokenizers. | |
| """ | |
| from __future__ import annotations | |
| import json | |
| from pathlib import Path | |
| from transformers import PreTrainedTokenizer | |
| from transformers.tokenization_utils_base import BatchEncoding | |
| try: | |
| # NOTE: HF's local-dir dynamic-module loader copies only THIS file's one level of relative | |
| # imports (no recursion), so the analyzer must be imported here even though only | |
| # our_tokenizer uses it. | |
| from .morph_tokenizer import load_resources as _hf_copy_probe # noqa: F401 | |
| from .our_tokenizer import BOS, EOS, OurTokenizer, PAD, SPECIALS, UNK # noqa: F401 | |
| except ImportError: # direct use (importlib evades HF check_imports regex) | |
| import importlib as _il | |
| _m = _il.import_module("our_tokenizer") | |
| BOS, EOS, OurTokenizer, PAD, SPECIALS, UNK = _m.BOS, _m.EOS, _m.OurTokenizer, _m.PAD, _m.SPECIALS, _m.UNK | |
| class OurMorphTokenizer(PreTrainedTokenizer): | |
| """Slow HF tokenizer backed by OurTokenizer. Emits offset_mapping for the strict harness.""" | |
| vocab_files_names = {"vocab_file": "our_vocab.json"} | |
| model_input_names = ["input_ids", "attention_mask"] | |
| def __init__(self, vocab_file=None, char_backoff=True, **kwargs): | |
| self._vocab = json.loads(Path(vocab_file).read_text(encoding="utf-8")) if vocab_file else {s: i for i, s in enumerate(SPECIALS)} | |
| self._ids_to_tokens = {i: t for t, i in self._vocab.items()} | |
| self._ot = OurTokenizer.load(vocab_file, res=None) if vocab_file else None | |
| if self._ot is not None: | |
| self._ot.char_backoff = char_backoff | |
| super().__init__(pad_token=PAD, unk_token=UNK, bos_token=BOS, eos_token=EOS, | |
| char_backoff=char_backoff, **kwargs) | |
| def vocab_size(self) -> int: | |
| return len(self._vocab) | |
| def get_vocab(self) -> dict: | |
| return dict(self._vocab) | |
| def _tokenize(self, text, **kwargs): | |
| return self._ot.tokenize(text) | |
| def _convert_token_to_id(self, token): | |
| return self._vocab.get(token, self._vocab[UNK]) | |
| def _convert_id_to_token(self, index): | |
| return self._ids_to_tokens.get(index, UNK) | |
| def convert_tokens_to_string(self, tokens): | |
| return self._ot.decode([self._convert_token_to_id(t) for t in tokens]) | |
| def build_inputs_with_special_tokens(self, token_ids_0, token_ids_1=None): | |
| # No auto special tokens (match the harness's no-BOS default); pairs are concatenated. | |
| return token_ids_0 if token_ids_1 is None else token_ids_0 + token_ids_1 | |
| def save_vocabulary(self, save_directory, filename_prefix=None): | |
| out = Path(save_directory) / ((filename_prefix + "-" if filename_prefix else "") + "our_vocab.json") | |
| out.write_text(json.dumps(self._vocab, ensure_ascii=False), encoding="utf-8") | |
| return (str(out),) | |
| # -- offset-aware encoding (the reason this wrapper exists) ------------------------------------- | |
| def _one(self, text, return_offsets_mapping): | |
| ids, spans = self._ot.encode_with_spans(text) | |
| d = {"input_ids": ids, "attention_mask": [1] * len(ids)} | |
| if return_offsets_mapping: | |
| d["offset_mapping"] = spans | |
| return d | |
| def _encode_plus(self, text, text_pair=None, add_special_tokens=True, return_offsets_mapping=False, | |
| return_attention_mask=None, return_tensors=None, **kwargs): | |
| d = self._one(text, return_offsets_mapping) | |
| if text_pair is not None: | |
| d2 = self._one(text_pair, return_offsets_mapping) | |
| d["input_ids"] += d2["input_ids"] | |
| d["attention_mask"] += d2["attention_mask"] | |
| if return_offsets_mapping: | |
| d["offset_mapping"] += d2["offset_mapping"] | |
| return BatchEncoding(d, tensor_type=return_tensors, prepend_batch_axis=True) | |
| def _batch_encode_plus(self, batch_text_or_pairs, add_special_tokens=True, return_offsets_mapping=False, | |
| return_attention_mask=None, return_tensors=None, padding_strategy=None, **kwargs): | |
| rows = [] | |
| for t in batch_text_or_pairs: | |
| text, pair = t if isinstance(t, tuple) else (t, None) | |
| r = self._one(text, return_offsets_mapping) | |
| if pair is not None: | |
| r2 = self._one(pair, return_offsets_mapping) | |
| r["input_ids"] += r2["input_ids"]; r["attention_mask"] += r2["attention_mask"] | |
| if return_offsets_mapping: | |
| r["offset_mapping"] += r2["offset_mapping"] | |
| rows.append(r) | |
| # left-as-lists (harness scores one at a time); pad only if a tensor type is requested | |
| out = {k: [r[k] for r in rows] for k in rows[0]} | |
| if return_tensors is not None: | |
| mx = max(len(r["input_ids"]) for r in rows) | |
| pad_id = self._vocab[PAD] | |
| out["input_ids"] = [r["input_ids"] + [pad_id] * (mx - len(r["input_ids"])) for r in rows] | |
| out["attention_mask"] = [r["attention_mask"] + [0] * (mx - len(r["attention_mask"])) for r in rows] | |
| if return_offsets_mapping: | |
| out["offset_mapping"] = [r["offset_mapping"] + [(0, 0)] * (mx - len(r["offset_mapping"])) for r in rows] | |
| return BatchEncoding(out, tensor_type=return_tensors) | |