File size: 3,088 Bytes
d4d4e7b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
"""Постобработка распознанного текста словарём якутского языка.

Прямое исправление текста спеллчекером вредит: на 5400 реальных строках оно
подняло долю ошибок с 0.24% до 0.89%, испортив 838 строк против 44 улучшенных.
Спеллчекер не видит изображение и «чинит» имена, русские вставки и обрывки
слов на переносах.

Поэтому словарь здесь только предлагает кандидатов, а решение принимает сама
сеть: замена принимается, если выигрыш языковой модели перевешивает потерю
по CTC. При весе 1.0 такая постобработка не испортила ни одной строки ни на
одной из проверочных выборок, снимая при этом 5–8% ошибок там, где
распознаватель слаб.

Пакет sakhaspell необязателен: без него обработка просто не применяется.
"""
import os

ALPHA = float(os.environ.get("SAKHA_LM_ALPHA", "1.0"))

_state = {"ready": False, "rescore": None}


def _load():
    if _state["ready"]:
        return _state["rescore"]
    _state["ready"] = True
    try:
        import sakhaspell  # noqa: F401
    except ImportError:
        return None
    try:
        from lm_rescore import Rescorer
    except ImportError:
        return None
    _state["rescore"] = Rescorer
    return Rescorer


class Corrector:
    """Ленивая обёртка: словарь и языковая модель грузятся при первом вызове."""

    def __init__(self, charset, alpha=ALPHA):
        self.charset = charset
        self.alpha = alpha
        self._r = None
        self._tried = False

    def available(self):
        if not self._tried:
            self._tried = True
            cls = _load()
            if cls is not None:
                try:
                    self._r = cls(self.charset, alpha=self.alpha)
                except Exception:
                    self._r = None
        return self._r is not None

    def __call__(self, texts, logprobs):
        """Строки идут в порядке чтения, поэтому перенос виден по предыдущей."""
        if not self.available():
            return texts
        out, prev_hyphen = [], False
        for t, lp in zip(texts, logprobs):
            if not t or lp is None:
                out.append(t)
                prev_hyphen = bool(t) and t.rstrip().endswith(("-", "\u2010", "\u2011"))
                continue
            try:
                out.append(self._r(lp, t, prev_hyphen=prev_hyphen))
            except Exception:
                out.append(t)
            prev_hyphen = t.rstrip().endswith(("-", "\u2010", "\u2011"))
        return out