chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
**Fundamental Text Processing (FunTextProcessing)**
|
||||
==========================
|
||||
|
||||
### Introduction
|
||||
|
||||
FunTextProcessing is a Python toolkit for fundamental text processing in ASR including text processing , inverse text processing, num2words, which is included in the `FunASR`.
|
||||
|
||||
### Highlights
|
||||
|
||||
- FunTextProcessing supports inverse text processing (ITN), text processing (TN), number to words (num2words).
|
||||
- FunTextProcessing supports multilingual, 10+ languages for ITN, 5 languages for TN, 50+ languages for num2words.
|
||||
|
||||
|
||||
### Example
|
||||
#### Inverse Text Processing (ITN)
|
||||
Given text inputs, such as speech recognition results, use `fun_text_processing/inverse_text_normalization/inverse_normalize.py` to output ITN results. You may refer to the following example scripts.
|
||||
|
||||
```
|
||||
test_file=fun_text_processing/inverse_text_normalization/id/id_itn_test_input.txt
|
||||
|
||||
python fun_text_processing/inverse_text_normalization/inverse_normalize.py --input_file $test_file --cache_dir ./itn_model/ --output_file output.txt --language=id
|
||||
```
|
||||
|
||||
|
||||
### Acknowledge
|
||||
1. We borrowed a lot of codes from [NeMo](https://github.com/NVIDIA/NeMo).
|
||||
2. We refered the codes from [WeTextProcessing](https://github.com/wenet-e2e/WeTextProcessing) for Chinese inverse text normalization.
|
||||
3. We borrowed a lot of codes from [num2words](https://pypi.org/project/num2words/) library for convert the number to words function in some languages.
|
||||
|
||||
### License
|
||||
|
||||
This project is licensed under the [The MIT License](https://opensource.org/licenses/MIT). FunTextProcessing also contains various third-party components and some code modified from other repos under other open source licenses.
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
if [[ $OSTYPE == 'darwin'* ]]; then
|
||||
conda install -c conda-forge -y pynini=2.1.5
|
||||
else
|
||||
pip install pynini==2.1.5
|
||||
fi
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
from fun_text_processing.inverse_text_normalization.en.taggers.tokenize_and_classify import (
|
||||
ClassifyFst,
|
||||
)
|
||||
from fun_text_processing.inverse_text_normalization.en.verbalizers.verbalize import VerbalizeFst
|
||||
from fun_text_processing.inverse_text_normalization.en.verbalizers.verbalize_final import (
|
||||
VerbalizeFinalFst,
|
||||
)
|
||||
@@ -0,0 +1,65 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import DAMO_SIGMA, GraphFst
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class CardinalFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying cardinals. Numbers below ten are not converted.
|
||||
Allows both compound numeral strings or separated by whitespace.
|
||||
"und" (en: "and") can be inserted between "hundert" and following number or "tausend" and following single or double digit number.
|
||||
|
||||
e.g. minus drei und zwanzig -> cardinal { negative: "-" integer: "23" } }
|
||||
e.g. minus dreiundzwanzig -> cardinal { integer: "23" } }
|
||||
e.g. dreizehn -> cardinal { integer: "13" } }
|
||||
e.g. ein hundert -> cardinal { integer: "100" } }
|
||||
e.g. einhundert -> cardinal { integer: "100" } }
|
||||
e.g. ein tausend -> cardinal { integer: "1000" } }
|
||||
e.g. eintausend -> cardinal { integer: "1000" } }
|
||||
e.g. ein tausend zwanzig -> cardinal { integer: "1020" } }
|
||||
|
||||
Args:
|
||||
tn_cardinal_tagger: TN cardinal tagger
|
||||
"""
|
||||
|
||||
def __init__(self, tn_cardinal_tagger: GraphFst, deterministic: bool = True):
|
||||
super().__init__(name="cardinal", kind="classify", deterministic=deterministic)
|
||||
|
||||
# add_space_between_chars = pynini.cdrewrite(pynini.closure(insert_space, 0, 1), DAMO_CHAR, DAMO_CHAR, DAMO_SIGMA)
|
||||
optional_delete_space = pynini.closure(DAMO_SIGMA | pynutil.delete(" "))
|
||||
|
||||
graph = (tn_cardinal_tagger.graph @ optional_delete_space).invert().optimize()
|
||||
self.graph_hundred_component_at_least_one_none_zero_digit = (
|
||||
(
|
||||
tn_cardinal_tagger.graph_hundred_component_at_least_one_none_zero_digit
|
||||
@ optional_delete_space
|
||||
)
|
||||
.invert()
|
||||
.optimize()
|
||||
)
|
||||
|
||||
self.graph_ties = (
|
||||
(tn_cardinal_tagger.two_digit_non_zero @ optional_delete_space).invert().optimize()
|
||||
)
|
||||
# this is to make sure if there is an ambiguity with decimal, decimal is chosen, e.g. 1000000 vs. 1 million
|
||||
graph = pynutil.add_weight(graph, weight=0.001)
|
||||
self.graph_no_exception = graph
|
||||
self.digit = (
|
||||
pynini.arcmap(tn_cardinal_tagger.digit, map_type="rmweight").invert().optimize()
|
||||
)
|
||||
graph_exception = pynini.project(self.digit, "input")
|
||||
self.graph = (pynini.project(graph, "input") - graph_exception.arcsort()) @ graph
|
||||
|
||||
self.optional_minus_graph = pynini.closure(
|
||||
pynutil.insert("negative: ") + pynini.cross("minus ", '"-" '), 0, 1
|
||||
)
|
||||
|
||||
final_graph = (
|
||||
self.optional_minus_graph
|
||||
+ pynutil.insert('integer: "')
|
||||
+ self.graph
|
||||
+ pynutil.insert('"')
|
||||
)
|
||||
|
||||
final_graph = self.add_tokens(final_graph)
|
||||
self.fst = final_graph.optimize()
|
||||
@@ -0,0 +1,84 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
DAMO_DIGIT,
|
||||
DAMO_NOT_QUOTE,
|
||||
DAMO_SIGMA,
|
||||
GraphFst,
|
||||
convert_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class DateFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying date, in the form of (day) month (year) or year
|
||||
e.g. vierundzwanzigster juli zwei tausend dreizehn -> tokens { name: "24. Jul. 2013" }
|
||||
e.g. neunzehnachtzig -> tokens { name: "1980" }
|
||||
e.g. vierzehnter januar -> tokens { name: "14. Jan." }
|
||||
e.g. zweiter dritter -> tokens { name: "02.03." }
|
||||
e.g. januar neunzehnachtzig -> tokens { name: "Jan. 1980" }
|
||||
e.g. zwanzigzwanzig -> tokens { name: "2020" }
|
||||
|
||||
Args:
|
||||
itn_cardinal_tagger: ITN cardinal tagger
|
||||
tn_date_tagger: TN date tagger
|
||||
tn_date_verbalizer: TN date verbalizer
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
itn_cardinal_tagger: GraphFst,
|
||||
tn_date_tagger: GraphFst,
|
||||
tn_date_verbalizer: GraphFst,
|
||||
deterministic: bool = True,
|
||||
):
|
||||
super().__init__(name="date", kind="classify", deterministic=deterministic)
|
||||
|
||||
add_leading_zero_to_double_digit = (DAMO_DIGIT + DAMO_DIGIT) | (
|
||||
pynutil.insert("0") + DAMO_DIGIT
|
||||
)
|
||||
optional_delete_space = pynini.closure(DAMO_SIGMA | pynutil.delete(" ", weight=0.0001))
|
||||
tagger = tn_date_verbalizer.graph.invert().optimize()
|
||||
|
||||
delete_day_marker = (
|
||||
pynutil.delete('day: "') + pynini.closure(DAMO_NOT_QUOTE, 1) + pynutil.delete('"')
|
||||
) @ itn_cardinal_tagger.graph_no_exception
|
||||
|
||||
month_as_number = (
|
||||
pynutil.delete('month: "')
|
||||
+ itn_cardinal_tagger.graph_no_exception
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
month_as_string = (
|
||||
pynutil.delete('month: "') + tn_date_tagger.month_abbr.invert() + pynutil.delete('"')
|
||||
)
|
||||
|
||||
convert_year = (tn_date_tagger.year @ optional_delete_space).invert().optimize()
|
||||
delete_year_marker = (
|
||||
pynutil.delete('year: "') + pynini.closure(DAMO_NOT_QUOTE, 1) + pynutil.delete('"')
|
||||
) @ convert_year
|
||||
|
||||
# day. month as string (year)
|
||||
verbalizer = (
|
||||
pynini.closure(delete_day_marker + pynutil.insert(".") + pynini.accep(" "), 0, 1)
|
||||
+ month_as_string
|
||||
+ pynini.closure(pynini.accep(" ") + delete_year_marker, 0, 1)
|
||||
)
|
||||
|
||||
# day. month as number (year)
|
||||
verbalizer |= (
|
||||
delete_day_marker @ add_leading_zero_to_double_digit
|
||||
+ pynutil.insert(".")
|
||||
+ pynutil.delete(" ")
|
||||
+ month_as_number @ add_leading_zero_to_double_digit
|
||||
+ pynutil.insert(".")
|
||||
+ pynini.closure(pynutil.delete(" ") + delete_year_marker, 0, 1)
|
||||
)
|
||||
|
||||
# year
|
||||
verbalizer |= delete_year_marker
|
||||
|
||||
final_graph = tagger @ verbalizer
|
||||
|
||||
graph = pynutil.insert('name: "') + convert_space(final_graph) + pynutil.insert('"')
|
||||
self.fst = graph.optimize()
|
||||
@@ -0,0 +1,52 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.de.taggers.decimal import get_quantity, quantities
|
||||
from fun_text_processing.text_normalization.en.graph_utils import DAMO_SIGMA, GraphFst
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class DecimalFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying decimal
|
||||
e.g. minus elf komma zwei null null sechs billionen -> decimal { negative: "true" integer_part: "11" fractional_part: "2006" quantity: "billionen" }
|
||||
e.g. eine billion -> decimal { integer_part: "1" quantity: "billion" }
|
||||
Args:
|
||||
itn_cardinal_tagger: ITN Cardinal tagger
|
||||
tn_decimal_tagger: TN decimal tagger
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, itn_cardinal_tagger: GraphFst, tn_decimal_tagger: GraphFst, deterministic: bool = True
|
||||
):
|
||||
super().__init__(name="decimal", kind="classify", deterministic=deterministic)
|
||||
|
||||
self.graph = tn_decimal_tagger.graph.invert().optimize()
|
||||
|
||||
delete_point = pynutil.delete(" komma")
|
||||
|
||||
allow_spelling = pynini.cdrewrite(
|
||||
pynini.cross("eine ", "eins ") + quantities, "[BOS]", "[EOS]", DAMO_SIGMA
|
||||
)
|
||||
|
||||
graph_fractional = pynutil.insert('fractional_part: "') + self.graph + pynutil.insert('"')
|
||||
graph_integer = (
|
||||
pynutil.insert('integer_part: "')
|
||||
+ itn_cardinal_tagger.graph_no_exception
|
||||
+ pynutil.insert('"')
|
||||
)
|
||||
final_graph_wo_sign = graph_integer + delete_point + pynini.accep(" ") + graph_fractional
|
||||
|
||||
self.final_graph_wo_negative = (
|
||||
allow_spelling
|
||||
@ (
|
||||
final_graph_wo_sign
|
||||
| get_quantity(
|
||||
final_graph_wo_sign,
|
||||
itn_cardinal_tagger.graph_hundred_component_at_least_one_none_zero_digit,
|
||||
)
|
||||
).optimize()
|
||||
)
|
||||
|
||||
final_graph = itn_cardinal_tagger.optional_minus_graph + self.final_graph_wo_negative
|
||||
final_graph += pynutil.insert(" preserve_order: true")
|
||||
final_graph = self.add_tokens(final_graph)
|
||||
self.fst = final_graph.optimize()
|
||||
@@ -0,0 +1,29 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import GraphFst
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class ElectronicFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying electronic: email addresses, etc.
|
||||
e.g. c d f eins at a b c punkt e d u -> tokens { name: "cdf1.abc.edu" }
|
||||
|
||||
Args:
|
||||
tn_electronic_tagger: TN eletronic tagger
|
||||
tn_electronic_verbalizer: TN eletronic verbalizer
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
tn_electronic_tagger: GraphFst,
|
||||
tn_electronic_verbalizer: GraphFst,
|
||||
deterministic: bool = True,
|
||||
):
|
||||
super().__init__(name="electronic", kind="classify", deterministic=deterministic)
|
||||
|
||||
tagger = pynini.invert(tn_electronic_verbalizer.graph).optimize()
|
||||
verbalizer = pynini.invert(tn_electronic_tagger.graph).optimize()
|
||||
final_graph = tagger @ verbalizer
|
||||
|
||||
graph = pynutil.insert('name: "') + final_graph + pynutil.insert('"')
|
||||
self.fst = graph.optimize()
|
||||
@@ -0,0 +1,66 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
DAMO_NOT_QUOTE,
|
||||
GraphFst,
|
||||
convert_space,
|
||||
delete_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class FractionFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying fraction
|
||||
e.g. ein halb -> tokens { name: "1/2" }
|
||||
e.g. ein ein halb -> tokens { name: "1 1/2" }
|
||||
e.g. drei zwei ein hundertstel -> tokens { name: "3 2/100" }
|
||||
|
||||
Args:
|
||||
itn_cardinal_tagger: ITN cardinal tagger
|
||||
tn_fraction_verbalizer: TN fraction verbalizer
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
itn_cardinal_tagger: GraphFst,
|
||||
tn_fraction_verbalizer: GraphFst,
|
||||
deterministic: bool = True,
|
||||
):
|
||||
super().__init__(name="fraction", kind="classify", deterministic=deterministic)
|
||||
tagger = tn_fraction_verbalizer.graph.invert().optimize()
|
||||
|
||||
delete_optional_sign = pynini.closure(
|
||||
pynutil.delete("negative: ") + pynini.cross('"true" ', "-"), 0, 1
|
||||
)
|
||||
delete_integer_marker = (
|
||||
pynutil.delete('integer_part: "')
|
||||
+ pynini.closure(DAMO_NOT_QUOTE, 1)
|
||||
+ pynutil.delete('"')
|
||||
) @ itn_cardinal_tagger.graph_no_exception
|
||||
|
||||
delete_numerator_marker = (
|
||||
pynutil.delete('numerator: "') + pynini.closure(DAMO_NOT_QUOTE, 1) + pynutil.delete('"')
|
||||
) @ itn_cardinal_tagger.graph_no_exception
|
||||
|
||||
delete_denominator_marker = (
|
||||
pynutil.insert("/")
|
||||
+ (
|
||||
pynutil.delete('denominator: "')
|
||||
+ pynini.closure(DAMO_NOT_QUOTE, 1)
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
@ itn_cardinal_tagger.graph_no_exception
|
||||
)
|
||||
|
||||
graph = (
|
||||
pynini.closure(delete_integer_marker + pynini.accep(" "), 0, 1)
|
||||
+ delete_numerator_marker
|
||||
+ delete_space
|
||||
+ delete_denominator_marker
|
||||
).optimize()
|
||||
verbalizer = delete_optional_sign + graph
|
||||
|
||||
self.graph = tagger @ verbalizer
|
||||
|
||||
graph = pynutil.insert('name: "') + convert_space(self.graph) + pynutil.insert('"')
|
||||
self.fst = graph.optimize()
|
||||
@@ -0,0 +1,98 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.de.taggers.measure import (
|
||||
singular_to_plural,
|
||||
unit_singular,
|
||||
)
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
DAMO_SIGMA,
|
||||
GraphFst,
|
||||
convert_space,
|
||||
delete_extra_space,
|
||||
delete_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class MeasureFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying measure. Allows for plural form for unit.
|
||||
e.g. minus elf kilogramm -> measure { cardinal { negative: "true" integer: "11" } units: "kg" }
|
||||
e.g. drei stunden -> measure { cardinal { integer: "3" } units: "h" }
|
||||
e.g. ein halb kilogramm -> measure { decimal { integer_part: "1/2" } units: "kg" }
|
||||
e.g. eins komma zwei kilogramm -> measure { decimal { integer_part: "1" fractional_part: "2" } units: "kg" }
|
||||
|
||||
Args:
|
||||
itn_cardinal_tagger: ITN Cardinal tagger
|
||||
itn_decimal_tagger: ITN Decimal tagger
|
||||
itn_fraction_tagger: ITN Fraction tagger
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
itn_cardinal_tagger: GraphFst,
|
||||
itn_decimal_tagger: GraphFst,
|
||||
itn_fraction_tagger: GraphFst,
|
||||
deterministic: bool = True,
|
||||
):
|
||||
super().__init__(name="measure", kind="classify", deterministic=deterministic)
|
||||
|
||||
cardinal_graph = (
|
||||
pynini.cdrewrite(
|
||||
pynini.cross(pynini.union("ein", "eine"), "eins"), "[BOS]", "[EOS]", DAMO_SIGMA
|
||||
)
|
||||
@ itn_cardinal_tagger.graph_no_exception
|
||||
)
|
||||
|
||||
graph_unit_singular = pynini.invert(unit_singular) # singular -> abbr
|
||||
unit = (
|
||||
pynini.invert(singular_to_plural()) @ graph_unit_singular
|
||||
) | graph_unit_singular # plural -> abbr
|
||||
unit = convert_space(unit)
|
||||
graph_unit_singular = convert_space(graph_unit_singular)
|
||||
|
||||
optional_graph_negative = pynini.closure(
|
||||
pynutil.insert("negative: ") + pynini.cross("minus", '"true"') + delete_extra_space,
|
||||
0,
|
||||
1,
|
||||
)
|
||||
|
||||
unit_misc = pynutil.insert("/") + pynutil.delete("pro") + delete_space + graph_unit_singular
|
||||
|
||||
unit = (
|
||||
pynutil.insert('units: "')
|
||||
+ (unit | unit_misc | pynutil.add_weight(unit + delete_space + unit_misc, 0.01))
|
||||
+ pynutil.insert('"')
|
||||
)
|
||||
|
||||
subgraph_decimal = (
|
||||
pynutil.insert("decimal { ")
|
||||
+ optional_graph_negative
|
||||
+ itn_decimal_tagger.final_graph_wo_negative
|
||||
+ pynutil.insert(" }")
|
||||
+ delete_extra_space
|
||||
+ unit
|
||||
)
|
||||
|
||||
subgraph_fraction = (
|
||||
pynutil.insert("decimal { ")
|
||||
+ optional_graph_negative
|
||||
+ pynutil.insert('integer_part: "')
|
||||
+ itn_fraction_tagger.graph
|
||||
+ pynutil.insert('" }')
|
||||
+ delete_extra_space
|
||||
+ unit
|
||||
)
|
||||
|
||||
subgraph_cardinal = (
|
||||
pynutil.insert("cardinal { ")
|
||||
+ optional_graph_negative
|
||||
+ pynutil.insert('integer: "')
|
||||
+ cardinal_graph
|
||||
+ pynutil.insert('"')
|
||||
+ pynutil.insert(" }")
|
||||
+ delete_extra_space
|
||||
+ unit
|
||||
)
|
||||
final_graph = subgraph_cardinal | subgraph_decimal | subgraph_fraction
|
||||
final_graph = self.add_tokens(final_graph)
|
||||
self.fst = final_graph.optimize()
|
||||
@@ -0,0 +1,90 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.de.taggers.money import (
|
||||
maj_singular,
|
||||
min_plural,
|
||||
min_singular,
|
||||
)
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
DAMO_DIGIT,
|
||||
DAMO_SIGMA,
|
||||
GraphFst,
|
||||
convert_space,
|
||||
delete_extra_space,
|
||||
delete_space,
|
||||
insert_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class MoneyFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying money
|
||||
e.g. elf euro und vier cent -> money { integer_part: "11" fractional_part: 04 currency: "€" }
|
||||
|
||||
Args:
|
||||
itn_cardinal_tagger: ITN Cardinal Tagger
|
||||
itn_decimal_tagger: ITN Decimal Tagger
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
itn_cardinal_tagger: GraphFst,
|
||||
itn_decimal_tagger: GraphFst,
|
||||
deterministic: bool = True,
|
||||
):
|
||||
super().__init__(name="money", kind="classify", deterministic=deterministic)
|
||||
cardinal_graph = (
|
||||
pynini.cdrewrite(
|
||||
pynini.cross(pynini.union("ein", "eine"), "eins"), "[BOS]", "[EOS]", DAMO_SIGMA
|
||||
)
|
||||
@ itn_cardinal_tagger.graph_no_exception
|
||||
)
|
||||
graph_decimal_final = itn_decimal_tagger.final_graph_wo_negative
|
||||
|
||||
graph_unit = pynini.invert(maj_singular)
|
||||
graph_unit = pynutil.insert('currency: "') + convert_space(graph_unit) + pynutil.insert('"')
|
||||
|
||||
add_leading_zero_to_double_digit = (DAMO_DIGIT + DAMO_DIGIT) | (
|
||||
pynutil.insert("0") + DAMO_DIGIT
|
||||
)
|
||||
min_unit = pynini.project(min_singular | min_plural, "output")
|
||||
# elf euro (und) vier cent, vier cent
|
||||
cents_standalone = (
|
||||
pynutil.insert('fractional_part: "')
|
||||
+ cardinal_graph @ add_leading_zero_to_double_digit
|
||||
+ delete_space
|
||||
+ pynutil.delete(min_unit)
|
||||
+ pynutil.insert('"')
|
||||
)
|
||||
|
||||
optional_cents_standalone = pynini.closure(
|
||||
delete_space
|
||||
+ pynini.closure(pynutil.delete("und") + delete_space, 0, 1)
|
||||
+ insert_space
|
||||
+ cents_standalone,
|
||||
0,
|
||||
1,
|
||||
)
|
||||
# elf euro vierzig, only after integer
|
||||
optional_cents_suffix = pynini.closure(
|
||||
delete_extra_space
|
||||
+ pynutil.insert('fractional_part: "')
|
||||
+ pynutil.add_weight(cardinal_graph @ add_leading_zero_to_double_digit, -0.7)
|
||||
+ pynutil.insert('"'),
|
||||
0,
|
||||
1,
|
||||
)
|
||||
|
||||
graph_integer = (
|
||||
pynutil.insert('integer_part: "')
|
||||
+ cardinal_graph
|
||||
+ pynutil.insert('"')
|
||||
+ delete_extra_space
|
||||
+ graph_unit
|
||||
+ (optional_cents_standalone | optional_cents_suffix)
|
||||
)
|
||||
graph_decimal = graph_decimal_final + delete_extra_space + graph_unit
|
||||
graph_decimal |= pynutil.insert('currency: "€" integer_part: "0" ') + cents_standalone
|
||||
final_graph = graph_integer | graph_decimal
|
||||
final_graph = self.add_tokens(final_graph)
|
||||
self.fst = final_graph.optimize()
|
||||
@@ -0,0 +1,33 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import DAMO_NOT_QUOTE, GraphFst
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class OrdinalFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying ordinal
|
||||
e.g. dreizehnter -> tokens { name: "13." }
|
||||
|
||||
Args:
|
||||
itn_cardinal_tagger: ITN Cardinal Tagger
|
||||
tn_ordinal_verbalizer: TN Ordinal Verbalizer
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
itn_cardinal_tagger: GraphFst,
|
||||
tn_ordinal_verbalizer: GraphFst,
|
||||
deterministic: bool = True,
|
||||
):
|
||||
super().__init__(name="ordinal", kind="classify", deterministic=deterministic)
|
||||
|
||||
tagger = tn_ordinal_verbalizer.graph.invert().optimize()
|
||||
|
||||
graph = (
|
||||
pynutil.delete('integer: "') + pynini.closure(DAMO_NOT_QUOTE, 1) + pynutil.delete('"')
|
||||
) @ itn_cardinal_tagger.graph
|
||||
|
||||
final_graph = tagger @ graph + pynutil.insert(".")
|
||||
|
||||
graph = pynutil.insert('name: "') + final_graph + pynutil.insert('"')
|
||||
self.fst = graph.optimize()
|
||||
@@ -0,0 +1,43 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
GraphFst,
|
||||
convert_space,
|
||||
insert_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class TelephoneFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying telephone numbers, e.g.
|
||||
null vier eins eins eins zwei drei vier eins zwei drei vier -> tokens { name: "(0411) 1234-1234" }
|
||||
|
||||
Args:
|
||||
tn_cardinal_tagger: TN Cardinal Tagger
|
||||
"""
|
||||
|
||||
def __init__(self, tn_cardinal_tagger: GraphFst, deterministic: bool = True):
|
||||
super().__init__(name="telephone", kind="classify", deterministic=deterministic)
|
||||
separator = pynini.accep(" ") # between components
|
||||
digit = pynini.union(*list(map(str, range(1, 10)))) @ tn_cardinal_tagger.two_digit_non_zero
|
||||
zero = pynini.cross("0", "null")
|
||||
|
||||
number_part = (
|
||||
pynutil.delete("(")
|
||||
+ zero
|
||||
+ insert_space
|
||||
+ pynini.closure(digit + insert_space, 2, 2)
|
||||
+ digit
|
||||
+ pynutil.delete(")")
|
||||
+ separator
|
||||
+ pynini.closure(digit + insert_space, 3, 3)
|
||||
+ digit
|
||||
+ pynutil.delete("-")
|
||||
+ insert_space
|
||||
+ pynini.closure(digit + insert_space, 3, 3)
|
||||
+ digit
|
||||
)
|
||||
graph = convert_space(pynini.invert(number_part))
|
||||
final_graph = pynutil.insert('name: "') + graph + pynutil.insert('"')
|
||||
|
||||
self.fst = final_graph.optimize()
|
||||
@@ -0,0 +1,28 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import DAMO_SIGMA, GraphFst
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class TimeFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying time
|
||||
e.g. acht uhr e s t-> time { hours: "8" zone: "e s t" }
|
||||
e.g. dreizehn uhr -> time { hours: "13" }
|
||||
e.g. dreizehn uhr zehn -> time { hours: "13" minutes: "10" }
|
||||
e.g. viertel vor zwölf -> time { minutes: "45" hours: "11" }
|
||||
e.g. viertel nach zwölf -> time { minutes: "15" hours: "12" }
|
||||
e.g. halb zwölf -> time { minutes: "30" hours: "11" }
|
||||
e.g. drei vor zwölf -> time { minutes: "57" hours: "11" }
|
||||
e.g. drei nach zwölf -> time { minutes: "3" hours: "12" }
|
||||
e.g. drei uhr zehn minuten zehn sekunden -> time { hours: "3" hours: "10" sekunden: "10"}
|
||||
|
||||
Args:
|
||||
tn_time_verbalizer: TN time verbalizer
|
||||
"""
|
||||
|
||||
def __init__(self, tn_time_verbalizer: GraphFst, deterministic: bool = True):
|
||||
super().__init__(name="time", kind="classify", deterministic=deterministic)
|
||||
# lazy way to make sure compounds work
|
||||
optional_delete_space = pynini.closure(DAMO_SIGMA | pynutil.delete(" ", weight=0.0001))
|
||||
graph = (tn_time_verbalizer.graph @ optional_delete_space).invert().optimize()
|
||||
self.fst = self.add_tokens(graph).optimize()
|
||||
@@ -0,0 +1,162 @@
|
||||
import os
|
||||
|
||||
import pynini
|
||||
from fun_text_processing.inverse_text_normalization.de.taggers.cardinal import CardinalFst
|
||||
from fun_text_processing.inverse_text_normalization.de.taggers.date import DateFst
|
||||
from fun_text_processing.inverse_text_normalization.de.taggers.decimal import DecimalFst
|
||||
from fun_text_processing.inverse_text_normalization.de.taggers.electronic import ElectronicFst
|
||||
from fun_text_processing.inverse_text_normalization.de.taggers.fraction import FractionFst
|
||||
from fun_text_processing.inverse_text_normalization.de.taggers.measure import MeasureFst
|
||||
from fun_text_processing.inverse_text_normalization.de.taggers.money import MoneyFst
|
||||
from fun_text_processing.inverse_text_normalization.de.taggers.ordinal import OrdinalFst
|
||||
from fun_text_processing.inverse_text_normalization.de.taggers.telephone import TelephoneFst
|
||||
from fun_text_processing.inverse_text_normalization.de.taggers.time import TimeFst
|
||||
from fun_text_processing.inverse_text_normalization.de.taggers.whitelist import WhiteListFst
|
||||
from fun_text_processing.inverse_text_normalization.en.taggers.punctuation import PunctuationFst
|
||||
from fun_text_processing.inverse_text_normalization.en.taggers.word import WordFst
|
||||
from fun_text_processing.text_normalization.de.taggers.cardinal import (
|
||||
CardinalFst as TNCardinalTagger,
|
||||
)
|
||||
from fun_text_processing.text_normalization.de.taggers.date import DateFst as TNDateTagger
|
||||
from fun_text_processing.text_normalization.de.taggers.decimal import DecimalFst as TNDecimalTagger
|
||||
from fun_text_processing.text_normalization.de.taggers.electronic import (
|
||||
ElectronicFst as TNElectronicTagger,
|
||||
)
|
||||
from fun_text_processing.text_normalization.de.taggers.whitelist import (
|
||||
WhiteListFst as TNWhitelistTagger,
|
||||
)
|
||||
from fun_text_processing.text_normalization.de.verbalizers.date import DateFst as TNDateVerbalizer
|
||||
from fun_text_processing.text_normalization.de.verbalizers.electronic import (
|
||||
ElectronicFst as TNElectronicVerbalizer,
|
||||
)
|
||||
from fun_text_processing.text_normalization.de.verbalizers.fraction import (
|
||||
FractionFst as TNFractionVerbalizer,
|
||||
)
|
||||
from fun_text_processing.text_normalization.de.verbalizers.ordinal import (
|
||||
OrdinalFst as TNOrdinalVerbalizer,
|
||||
)
|
||||
from fun_text_processing.text_normalization.de.verbalizers.time import TimeFst as TNTimeVerbalizer
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
GraphFst,
|
||||
delete_extra_space,
|
||||
delete_space,
|
||||
generator_main,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
import logging
|
||||
|
||||
|
||||
class ClassifyFst(GraphFst):
|
||||
"""
|
||||
Final class that composes all other classification grammars. This class can process an entire sentence, that is lower cased.
|
||||
For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File.
|
||||
More details to deployment at NeMo/tools/text_processing_deployment.
|
||||
|
||||
Args:
|
||||
cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache.
|
||||
overwrite_cache: set to True to overwrite .far files
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, cache_dir: str = None, overwrite_cache: bool = False, deterministic: bool = True
|
||||
):
|
||||
super().__init__(name="tokenize_and_classify", kind="classify", deterministic=deterministic)
|
||||
|
||||
far_file = None
|
||||
if cache_dir is not None and cache_dir != "None":
|
||||
os.makedirs(cache_dir, exist_ok=True)
|
||||
far_file = os.path.join(cache_dir, "_de_itn.far")
|
||||
if not overwrite_cache and far_file and os.path.exists(far_file):
|
||||
self.fst = pynini.Far(far_file, mode="r")["tokenize_and_classify"]
|
||||
logging.info(f"ClassifyFst.fst was restored from {far_file}.")
|
||||
else:
|
||||
logging.info(f"Creating ClassifyFst grammars.")
|
||||
tn_cardinal_tagger = TNCardinalTagger(deterministic=False)
|
||||
tn_date_tagger = TNDateTagger(cardinal=tn_cardinal_tagger, deterministic=False)
|
||||
tn_decimal_tagger = TNDecimalTagger(cardinal=tn_cardinal_tagger, deterministic=False)
|
||||
tn_ordinal_verbalizer = TNOrdinalVerbalizer(deterministic=False)
|
||||
tn_fraction_verbalizer = TNFractionVerbalizer(
|
||||
ordinal=tn_ordinal_verbalizer, deterministic=False
|
||||
)
|
||||
tn_time_verbalizer = TNTimeVerbalizer(
|
||||
cardinal_tagger=tn_cardinal_tagger, deterministic=False
|
||||
)
|
||||
tn_date_verbalizer = TNDateVerbalizer(
|
||||
ordinal=tn_ordinal_verbalizer, deterministic=False
|
||||
)
|
||||
tn_electronic_tagger = TNElectronicTagger(deterministic=False)
|
||||
tn_electronic_verbalizer = TNElectronicVerbalizer(deterministic=False)
|
||||
tn_whitelist_tagger = TNWhitelistTagger(input_case="cased", deterministic=False)
|
||||
|
||||
cardinal = CardinalFst(tn_cardinal_tagger=tn_cardinal_tagger)
|
||||
cardinal_graph = cardinal.fst
|
||||
|
||||
ordinal = OrdinalFst(
|
||||
itn_cardinal_tagger=cardinal, tn_ordinal_verbalizer=tn_ordinal_verbalizer
|
||||
)
|
||||
ordinal_graph = ordinal.fst
|
||||
decimal = DecimalFst(itn_cardinal_tagger=cardinal, tn_decimal_tagger=tn_decimal_tagger)
|
||||
decimal_graph = decimal.fst
|
||||
|
||||
fraction = FractionFst(
|
||||
itn_cardinal_tagger=cardinal, tn_fraction_verbalizer=tn_fraction_verbalizer
|
||||
)
|
||||
fraction_graph = fraction.fst
|
||||
|
||||
measure_graph = MeasureFst(
|
||||
itn_cardinal_tagger=cardinal,
|
||||
itn_decimal_tagger=decimal,
|
||||
itn_fraction_tagger=fraction,
|
||||
).fst
|
||||
date_graph = DateFst(
|
||||
itn_cardinal_tagger=cardinal,
|
||||
tn_date_verbalizer=tn_date_verbalizer,
|
||||
tn_date_tagger=tn_date_tagger,
|
||||
).fst
|
||||
word_graph = WordFst().fst
|
||||
time_graph = TimeFst(tn_time_verbalizer=tn_time_verbalizer).fst
|
||||
money_graph = MoneyFst(itn_cardinal_tagger=cardinal, itn_decimal_tagger=decimal).fst
|
||||
whitelist_graph = WhiteListFst(tn_whitelist_tagger=tn_whitelist_tagger).fst
|
||||
punct_graph = PunctuationFst().fst
|
||||
electronic_graph = ElectronicFst(
|
||||
tn_electronic_tagger=tn_electronic_tagger,
|
||||
tn_electronic_verbalizer=tn_electronic_verbalizer,
|
||||
).fst
|
||||
telephone_graph = TelephoneFst(tn_cardinal_tagger=tn_cardinal_tagger).fst
|
||||
|
||||
classify = (
|
||||
pynutil.add_weight(cardinal_graph, 1.1)
|
||||
| pynutil.add_weight(whitelist_graph, 1.0)
|
||||
| pynutil.add_weight(time_graph, 1.1)
|
||||
| pynutil.add_weight(date_graph, 1.1)
|
||||
| pynutil.add_weight(decimal_graph, 1.1)
|
||||
| pynutil.add_weight(measure_graph, 1.1)
|
||||
| pynutil.add_weight(ordinal_graph, 1.1)
|
||||
| pynutil.add_weight(fraction_graph, 1.1)
|
||||
| pynutil.add_weight(money_graph, 1.1)
|
||||
| pynutil.add_weight(telephone_graph, 1.1)
|
||||
| pynutil.add_weight(electronic_graph, 1.1)
|
||||
| pynutil.add_weight(word_graph, 100)
|
||||
)
|
||||
|
||||
punct = (
|
||||
pynutil.insert("tokens { ")
|
||||
+ pynutil.add_weight(punct_graph, weight=1.1)
|
||||
+ pynutil.insert(" }")
|
||||
)
|
||||
token = pynutil.insert("tokens { ") + classify + pynutil.insert(" }")
|
||||
token_plus_punct = (
|
||||
pynini.closure(punct + pynutil.insert(" "))
|
||||
+ token
|
||||
+ pynini.closure(pynutil.insert(" ") + punct)
|
||||
)
|
||||
|
||||
graph = token_plus_punct + pynini.closure(delete_extra_space + token_plus_punct)
|
||||
graph = delete_space + graph + delete_space
|
||||
|
||||
self.fst = graph.optimize()
|
||||
|
||||
if far_file:
|
||||
generator_main(far_file, {"tokenize_and_classify": self.fst})
|
||||
logging.info(f"ClassifyFst grammars are saved to {far_file}.")
|
||||
@@ -0,0 +1,19 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import GraphFst, convert_space
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class WhiteListFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying whitelisted tokens
|
||||
e.g. misses -> tokens { name: "Mrs." }
|
||||
Args:
|
||||
tn_whitelist_tagger: TN whitelist tagger
|
||||
"""
|
||||
|
||||
def __init__(self, tn_whitelist_tagger: GraphFst, deterministic: bool = True):
|
||||
super().__init__(name="whitelist", kind="classify", deterministic=deterministic)
|
||||
|
||||
whitelist = pynini.invert(tn_whitelist_tagger.graph)
|
||||
graph = pynutil.insert('name: "') + convert_space(whitelist) + pynutil.insert('"')
|
||||
self.fst = graph.optimize()
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import DAMO_NOT_QUOTE, GraphFst
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class CardinalFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for verbalizing cardinal
|
||||
e.g. cardinal { integer: "23" negative: "-" } -> -23
|
||||
|
||||
Args:
|
||||
tn_cardinal_verbalizer: TN cardinal verbalizer
|
||||
"""
|
||||
|
||||
def __init__(self, tn_cardinal_verbalizer: GraphFst, deterministic: bool = True):
|
||||
super().__init__(name="cardinal", kind="verbalize", deterministic=deterministic)
|
||||
self.numbers = tn_cardinal_verbalizer.numbers
|
||||
optional_sign = pynini.closure(
|
||||
pynutil.delete('negative: "') + DAMO_NOT_QUOTE + pynutil.delete('" '), 0, 1
|
||||
)
|
||||
graph = optional_sign + self.numbers
|
||||
delete_tokens = self.delete_tokens(graph)
|
||||
self.fst = delete_tokens.optimize()
|
||||
@@ -0,0 +1,37 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
DAMO_NOT_QUOTE,
|
||||
GraphFst,
|
||||
delete_preserve_order,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class DecimalFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for verbalizing decimal, e.g.
|
||||
decimal { negative: "true" integer_part: "12" fractional_part: "5006" quantity: "billion" } -> -12.5006 billion
|
||||
|
||||
Args:
|
||||
tn_decimal_verbalizer: TN decimal verbalizer
|
||||
"""
|
||||
|
||||
def __init__(self, tn_decimal_verbalizer: GraphFst, deterministic: bool = True):
|
||||
super().__init__(name="decimal", kind="verbalize", deterministic=deterministic)
|
||||
delete_space = pynutil.delete(" ")
|
||||
optional_sign = pynini.closure(
|
||||
pynutil.delete('negative: "') + DAMO_NOT_QUOTE + pynutil.delete('"') + delete_space,
|
||||
0,
|
||||
1,
|
||||
)
|
||||
optional_integer = pynini.closure(tn_decimal_verbalizer.integer, 0, 1)
|
||||
optional_fractional = pynini.closure(
|
||||
delete_space + pynutil.insert(",") + tn_decimal_verbalizer.fractional_default, 0, 1
|
||||
)
|
||||
graph = (
|
||||
optional_integer + optional_fractional + tn_decimal_verbalizer.optional_quantity
|
||||
).optimize()
|
||||
self.numbers = optional_sign + graph
|
||||
graph = self.numbers + delete_preserve_order
|
||||
delete_tokens = self.delete_tokens(graph)
|
||||
self.fst = delete_tokens.optimize()
|
||||
@@ -0,0 +1,50 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import DAMO_CHAR, GraphFst, delete_space
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class MeasureFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for verbalizing measure, e.g.
|
||||
measure { cardinal { negative: "true" integer: "12" } units: "kg" } -> -12 kg
|
||||
measure { decimal { integer_part: "1/2" } units: "kg" } -> 1/2 kg
|
||||
measure { decimal { integer_part: "1" fractional_part: "2" quantity: "million" } units: "kg" } -> 1,2 million kg
|
||||
|
||||
Args:
|
||||
decimal: ITN Decimal verbalizer
|
||||
cardinal: ITN Cardinal verbalizer
|
||||
"""
|
||||
|
||||
def __init__(self, decimal: GraphFst, cardinal: GraphFst, deterministic: bool = True):
|
||||
super().__init__(name="measure", kind="verbalize", deterministic=deterministic)
|
||||
optional_sign = pynini.closure(pynini.cross('negative: "true"', "-"), 0, 1)
|
||||
unit = (
|
||||
pynutil.delete("units:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.closure(DAMO_CHAR - " ", 1)
|
||||
+ pynutil.delete('"')
|
||||
+ delete_space
|
||||
)
|
||||
graph_decimal = (
|
||||
pynutil.delete("decimal {")
|
||||
+ delete_space
|
||||
+ optional_sign
|
||||
+ delete_space
|
||||
+ decimal.numbers
|
||||
+ delete_space
|
||||
+ pynutil.delete("}")
|
||||
)
|
||||
graph_cardinal = (
|
||||
pynutil.delete("cardinal {")
|
||||
+ delete_space
|
||||
+ optional_sign
|
||||
+ delete_space
|
||||
+ cardinal.numbers
|
||||
+ delete_space
|
||||
+ pynutil.delete("}")
|
||||
)
|
||||
|
||||
graph = (graph_cardinal | graph_decimal) + delete_space + pynutil.insert(" ") + unit
|
||||
delete_tokens = self.delete_tokens(graph)
|
||||
self.fst = delete_tokens.optimize()
|
||||
@@ -0,0 +1,26 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import DAMO_CHAR, GraphFst, delete_space
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class MoneyFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for verbalizing money, e.g.
|
||||
money { integer_part: "12" fractional_part: "05" currency: "$" } -> $12.05
|
||||
|
||||
Args:
|
||||
decimal: ITN Decimal verbalizer
|
||||
"""
|
||||
|
||||
def __init__(self, decimal: GraphFst, deterministic: bool = True):
|
||||
super().__init__(name="money", kind="verbalize", deterministic=deterministic)
|
||||
unit = (
|
||||
pynutil.delete("currency:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.closure(DAMO_CHAR - " ", 1)
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
graph = unit + delete_space + decimal.numbers
|
||||
delete_tokens = self.delete_tokens(graph)
|
||||
self.fst = delete_tokens.optimize()
|
||||
@@ -0,0 +1,52 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
DAMO_ALPHA,
|
||||
DAMO_DIGIT,
|
||||
GraphFst,
|
||||
delete_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class TimeFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for verbalizing time, e.g.
|
||||
time { hours: "8" minutes: "30" zone: "e s t" } -> 08:30 Uhr est
|
||||
time { hours: "8" } -> 8 Uhr
|
||||
time { hours: "8" minutes: "30" seconds: "10" } -> 08:30:10 Uhr
|
||||
"""
|
||||
|
||||
def __init__(self, deterministic: bool = True):
|
||||
super().__init__(name="time", kind="verbalize", deterministic=deterministic)
|
||||
|
||||
add_leading_zero_to_double_digit = (DAMO_DIGIT + DAMO_DIGIT) | (
|
||||
pynutil.insert("0") + DAMO_DIGIT
|
||||
)
|
||||
hour = pynutil.delete('hours: "') + pynini.closure(DAMO_DIGIT, 1) + pynutil.delete('"')
|
||||
minute = pynutil.delete('minutes: "') + pynini.closure(DAMO_DIGIT, 1) + pynutil.delete('"')
|
||||
|
||||
second = pynutil.delete('seconds: "') + pynini.closure(DAMO_DIGIT, 1) + pynutil.delete('"')
|
||||
zone = (
|
||||
pynutil.delete('zone: "')
|
||||
+ pynini.closure(DAMO_ALPHA + delete_space)
|
||||
+ DAMO_ALPHA
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
optional_zone = pynini.closure(pynini.accep(" ") + zone, 0, 1)
|
||||
graph = (
|
||||
delete_space
|
||||
+ pynutil.insert(":")
|
||||
+ (minute @ add_leading_zero_to_double_digit)
|
||||
+ pynini.closure(
|
||||
delete_space + pynutil.insert(":") + (second @ add_leading_zero_to_double_digit),
|
||||
0,
|
||||
1,
|
||||
)
|
||||
+ pynutil.insert(" Uhr")
|
||||
+ optional_zone
|
||||
)
|
||||
graph_h = hour + pynutil.insert(" Uhr") + optional_zone
|
||||
graph_hm = hour @ add_leading_zero_to_double_digit + graph
|
||||
graph_hms = hour @ add_leading_zero_to_double_digit + graph
|
||||
final_graph = graph_hm | graph_hms | graph_h
|
||||
self.fst = self.delete_tokens(final_graph).optimize()
|
||||
@@ -0,0 +1,35 @@
|
||||
from fun_text_processing.inverse_text_normalization.de.verbalizers.cardinal import CardinalFst
|
||||
from fun_text_processing.inverse_text_normalization.de.verbalizers.decimal import DecimalFst
|
||||
from fun_text_processing.inverse_text_normalization.de.verbalizers.measure import MeasureFst
|
||||
from fun_text_processing.inverse_text_normalization.de.verbalizers.money import MoneyFst
|
||||
from fun_text_processing.inverse_text_normalization.de.verbalizers.time import TimeFst
|
||||
from fun_text_processing.text_normalization.de.verbalizers.cardinal import (
|
||||
CardinalFst as TNCardinalVerbalizer,
|
||||
)
|
||||
from fun_text_processing.text_normalization.de.verbalizers.decimal import (
|
||||
DecimalFst as TNDecimalVerbalizer,
|
||||
)
|
||||
from fun_text_processing.text_normalization.en.graph_utils import GraphFst
|
||||
|
||||
|
||||
class VerbalizeFst(GraphFst):
|
||||
"""
|
||||
Composes other verbalizer grammars.
|
||||
For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File.
|
||||
More details to deployment at NeMo/tools/text_processing_deployment.
|
||||
"""
|
||||
|
||||
def __init__(self, deterministic: bool = True):
|
||||
super().__init__(name="verbalize", kind="verbalize", deterministic=deterministic)
|
||||
tn_cardinal_verbalizer = TNCardinalVerbalizer(deterministic=False)
|
||||
tn_decimal_verbalizer = TNDecimalVerbalizer(deterministic=False)
|
||||
|
||||
cardinal = CardinalFst(tn_cardinal_verbalizer=tn_cardinal_verbalizer)
|
||||
cardinal_graph = cardinal.fst
|
||||
decimal = DecimalFst(tn_decimal_verbalizer=tn_decimal_verbalizer)
|
||||
decimal_graph = decimal.fst
|
||||
measure_graph = MeasureFst(decimal=decimal, cardinal=cardinal).fst
|
||||
money_graph = MoneyFst(decimal=decimal).fst
|
||||
time_graph = TimeFst().fst
|
||||
graph = time_graph | money_graph | measure_graph | decimal_graph | cardinal_graph
|
||||
self.fst = graph
|
||||
@@ -0,0 +1,33 @@
|
||||
import pynini
|
||||
from fun_text_processing.inverse_text_normalization.de.verbalizers.verbalize import VerbalizeFst
|
||||
from fun_text_processing.inverse_text_normalization.en.verbalizers.word import WordFst
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
GraphFst,
|
||||
delete_extra_space,
|
||||
delete_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class VerbalizeFinalFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer that verbalizes an entire sentence, e.g.
|
||||
tokens { name: "jetzt" } tokens { name: "ist" } tokens { time { hours: "12" minutes: "30" } } -> jetzt ist 12:30 Uhr
|
||||
"""
|
||||
|
||||
def __init__(self, deterministic: bool = True):
|
||||
super().__init__(name="verbalize_final", kind="verbalize", deterministic=deterministic)
|
||||
verbalize = VerbalizeFst().fst
|
||||
word = WordFst().fst
|
||||
types = verbalize | word
|
||||
graph = (
|
||||
pynutil.delete("tokens")
|
||||
+ delete_space
|
||||
+ pynutil.delete("{")
|
||||
+ delete_space
|
||||
+ types
|
||||
+ delete_space
|
||||
+ pynutil.delete("}")
|
||||
)
|
||||
graph = delete_space + pynini.closure(graph + delete_extra_space) + graph + delete_space
|
||||
self.fst = graph
|
||||
@@ -0,0 +1,7 @@
|
||||
from fun_text_processing.inverse_text_normalization.en.taggers.tokenize_and_classify import (
|
||||
ClassifyFst,
|
||||
)
|
||||
from fun_text_processing.inverse_text_normalization.en.verbalizers.verbalize import VerbalizeFst
|
||||
from fun_text_processing.inverse_text_normalization.en.verbalizers.verbalize_final import (
|
||||
VerbalizeFinalFst,
|
||||
)
|
||||
@@ -0,0 +1,384 @@
|
||||
from argparse import ArgumentParser
|
||||
from typing import List
|
||||
|
||||
import regex as re
|
||||
from fun_text_processing.text_normalization.data_loader_utils import (
|
||||
EOS_TYPE,
|
||||
Instance,
|
||||
load_files,
|
||||
training_data_to_sentences,
|
||||
)
|
||||
|
||||
|
||||
"""
|
||||
This file is for evaluation purposes.
|
||||
filter_loaded_data() cleans data (list of instances) for inverse text normalization. Filters and cleaners can be specified for each semiotic class individually.
|
||||
For example, normalized text should only include characters and whitespace characters but no punctuation.
|
||||
Cardinal unnormalized instances should contain at least one integer and all other characters are removed.
|
||||
"""
|
||||
|
||||
|
||||
class Filter:
|
||||
"""
|
||||
Filter class
|
||||
|
||||
Args:
|
||||
class_type: semiotic class used in dataset
|
||||
process_func: function to transform text
|
||||
filter_func: function to filter text
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, class_type: str, process_func: object, filter_func: object):
|
||||
self.class_type = class_type
|
||||
self.process_func = process_func
|
||||
self.filter_func = filter_func
|
||||
|
||||
def filter(self, instance: Instance) -> bool:
|
||||
"""
|
||||
filter function
|
||||
|
||||
Args:
|
||||
filters given instance with filter function
|
||||
|
||||
Returns: True if given instance fulfills criteria or does not belong to class type
|
||||
"""
|
||||
if instance.token_type != self.class_type:
|
||||
return True
|
||||
return self.filter_func(instance)
|
||||
|
||||
def process(self, instance: Instance) -> Instance:
|
||||
"""
|
||||
process function
|
||||
|
||||
Args:
|
||||
processes given instance with process function
|
||||
|
||||
Returns: processed instance if instance belongs to expected class type or original instance
|
||||
"""
|
||||
if instance.token_type != self.class_type:
|
||||
return instance
|
||||
return self.process_func(instance)
|
||||
|
||||
|
||||
def filter_cardinal_1(instance: Instance) -> bool:
|
||||
ok = re.search(r"[0-9]", instance.un_normalized)
|
||||
return ok
|
||||
|
||||
|
||||
def process_cardinal_1(instance: Instance) -> Instance:
|
||||
un_normalized = instance.un_normalized
|
||||
normalized = instance.normalized
|
||||
un_normalized = re.sub(r"[^0-9]", "", un_normalized)
|
||||
normalized = re.sub(r"[^a-z ]", "", normalized)
|
||||
return Instance(
|
||||
token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized
|
||||
)
|
||||
|
||||
|
||||
def filter_ordinal_1(instance: Instance) -> bool:
|
||||
ok = re.search(r"(st|nd|rd|th)\s*$", instance.un_normalized)
|
||||
return ok
|
||||
|
||||
|
||||
def process_ordinal_1(instance: Instance) -> Instance:
|
||||
un_normalized = instance.un_normalized
|
||||
normalized = instance.normalized
|
||||
un_normalized = re.sub(r"[,\s]", "", un_normalized)
|
||||
normalized = re.sub(r"[^a-z ]", "", normalized)
|
||||
return Instance(
|
||||
token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized
|
||||
)
|
||||
|
||||
|
||||
def filter_decimal_1(instance: Instance) -> bool:
|
||||
ok = re.search(r"[0-9]", instance.un_normalized)
|
||||
return ok
|
||||
|
||||
|
||||
def process_decimal_1(instance: Instance) -> Instance:
|
||||
un_normalized = instance.un_normalized
|
||||
un_normalized = re.sub(r",", "", un_normalized)
|
||||
normalized = instance.normalized
|
||||
normalized = re.sub(r"[^a-z ]", "", normalized)
|
||||
return Instance(
|
||||
token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized
|
||||
)
|
||||
|
||||
|
||||
def filter_measure_1(instance: Instance) -> bool:
|
||||
ok = True
|
||||
return ok
|
||||
|
||||
|
||||
def process_measure_1(instance: Instance) -> Instance:
|
||||
un_normalized = instance.un_normalized
|
||||
normalized = instance.normalized
|
||||
un_normalized = re.sub(r",", "", un_normalized)
|
||||
un_normalized = re.sub(r"m2", "m²", un_normalized)
|
||||
un_normalized = re.sub(r"(\d)([^\d.\s])", r"\1 \2", un_normalized)
|
||||
normalized = re.sub(r"[^a-z\s]", "", normalized)
|
||||
normalized = re.sub(r"per ([a-z\s]*)s$", r"per \1", normalized)
|
||||
normalized = re.sub(r"[^a-z ]", "", normalized)
|
||||
return Instance(
|
||||
token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized
|
||||
)
|
||||
|
||||
|
||||
def filter_money_1(instance: Instance) -> bool:
|
||||
ok = re.search(r"[0-9]", instance.un_normalized)
|
||||
return ok
|
||||
|
||||
|
||||
def process_money_1(instance: Instance) -> Instance:
|
||||
un_normalized = instance.un_normalized
|
||||
normalized = instance.normalized
|
||||
un_normalized = re.sub(r",", "", un_normalized)
|
||||
un_normalized = re.sub(r"a\$", r"$", un_normalized)
|
||||
un_normalized = re.sub(r"us\$", r"$", un_normalized)
|
||||
un_normalized = re.sub(r"(\d)m\s*$", r"\1 million", un_normalized)
|
||||
un_normalized = re.sub(r"(\d)bn?\s*$", r"\1 billion", un_normalized)
|
||||
normalized = re.sub(r"[^a-z ]", "", normalized)
|
||||
return Instance(
|
||||
token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized
|
||||
)
|
||||
|
||||
|
||||
def filter_time_1(instance: Instance) -> bool:
|
||||
ok = re.search(r"[0-9]", instance.un_normalized)
|
||||
return ok
|
||||
|
||||
|
||||
def process_time_1(instance: Instance) -> Instance:
|
||||
un_normalized = instance.un_normalized
|
||||
un_normalized = re.sub(r": ", ":", un_normalized)
|
||||
un_normalized = re.sub(r"(\d)\s?a\s?m\s?", r"\1 a.m.", un_normalized)
|
||||
un_normalized = re.sub(r"(\d)\s?p\s?m\s?", r"\1 p.m.", un_normalized)
|
||||
normalized = instance.normalized
|
||||
normalized = re.sub(r"[^a-z ]", "", normalized)
|
||||
return Instance(
|
||||
token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized
|
||||
)
|
||||
|
||||
|
||||
def filter_plain_1(instance: Instance) -> bool:
|
||||
ok = True
|
||||
return ok
|
||||
|
||||
|
||||
def process_plain_1(instance: Instance) -> Instance:
|
||||
un_normalized = instance.un_normalized
|
||||
normalized = instance.normalized
|
||||
return Instance(
|
||||
token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized
|
||||
)
|
||||
|
||||
|
||||
def filter_punct_1(instance: Instance) -> bool:
|
||||
ok = True
|
||||
return ok
|
||||
|
||||
|
||||
def process_punct_1(instance: Instance) -> Instance:
|
||||
un_normalized = instance.un_normalized
|
||||
normalized = instance.normalized
|
||||
return Instance(
|
||||
token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized
|
||||
)
|
||||
|
||||
|
||||
def filter_date_1(instance: Instance) -> bool:
|
||||
ok = True
|
||||
return ok
|
||||
|
||||
|
||||
def process_date_1(instance: Instance) -> Instance:
|
||||
un_normalized = instance.un_normalized
|
||||
un_normalized = re.sub(r",", "", un_normalized)
|
||||
normalized = instance.normalized
|
||||
normalized = re.sub(r"[^a-z ]", "", normalized)
|
||||
return Instance(
|
||||
token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized
|
||||
)
|
||||
|
||||
|
||||
def filter_letters_1(instance: Instance) -> bool:
|
||||
ok = True
|
||||
return ok
|
||||
|
||||
|
||||
def process_letters_1(instance: Instance) -> Instance:
|
||||
un_normalized = instance.un_normalized
|
||||
normalized = instance.normalized
|
||||
normalized = re.sub(r"[^a-z ]", "", normalized)
|
||||
return Instance(
|
||||
token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized
|
||||
)
|
||||
|
||||
|
||||
def filter_verbatim_1(instance: Instance) -> bool:
|
||||
ok = True
|
||||
return ok
|
||||
|
||||
|
||||
def process_verbatim_1(instance: Instance) -> Instance:
|
||||
un_normalized = instance.un_normalized
|
||||
normalized = instance.normalized
|
||||
return Instance(
|
||||
token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized
|
||||
)
|
||||
|
||||
|
||||
def filter_digit_1(instance: Instance) -> bool:
|
||||
ok = re.search(r"[0-9]", instance.un_normalized)
|
||||
return ok
|
||||
|
||||
|
||||
def process_digit_1(instance: Instance) -> Instance:
|
||||
un_normalized = instance.un_normalized
|
||||
normalized = instance.normalized
|
||||
normalized = re.sub(r"[^a-z ]", "", normalized)
|
||||
return Instance(
|
||||
token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized
|
||||
)
|
||||
|
||||
|
||||
def filter_telephone_1(instance: Instance) -> bool:
|
||||
ok = re.search(r"[0-9]", instance.un_normalized)
|
||||
return ok
|
||||
|
||||
|
||||
def process_telephone_1(instance: Instance) -> Instance:
|
||||
un_normalized = instance.un_normalized
|
||||
normalized = instance.normalized
|
||||
normalized = re.sub(r"[^a-z ]", "", normalized)
|
||||
return Instance(
|
||||
token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized
|
||||
)
|
||||
|
||||
|
||||
def filter_electronic_1(instance: Instance) -> bool:
|
||||
ok = re.search(r"[0-9]", instance.un_normalized)
|
||||
return ok
|
||||
|
||||
|
||||
def process_electronic_1(instance: Instance) -> Instance:
|
||||
un_normalized = instance.un_normalized
|
||||
normalized = instance.normalized
|
||||
normalized = re.sub(r"[^a-z ]", "", normalized)
|
||||
return Instance(
|
||||
token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized
|
||||
)
|
||||
|
||||
|
||||
def filter_fraction_1(instance: Instance) -> bool:
|
||||
ok = re.search(r"[0-9]", instance.un_normalized)
|
||||
return ok
|
||||
|
||||
|
||||
def process_fraction_1(instance: Instance) -> Instance:
|
||||
un_normalized = instance.un_normalized
|
||||
normalized = instance.normalized
|
||||
normalized = re.sub(r"[^a-z ]", "", normalized)
|
||||
return Instance(
|
||||
token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized
|
||||
)
|
||||
|
||||
|
||||
def filter_address_1(instance: Instance) -> bool:
|
||||
ok = True
|
||||
return ok
|
||||
|
||||
|
||||
def process_address_1(instance: Instance) -> Instance:
|
||||
un_normalized = instance.un_normalized
|
||||
normalized = instance.normalized
|
||||
normalized = re.sub(r"[^a-z ]", "", normalized)
|
||||
return Instance(
|
||||
token_type=instance.token_type, un_normalized=un_normalized, normalized=normalized
|
||||
)
|
||||
|
||||
|
||||
filters = []
|
||||
filters.append(
|
||||
Filter(class_type="CARDINAL", process_func=process_cardinal_1, filter_func=filter_cardinal_1)
|
||||
)
|
||||
filters.append(
|
||||
Filter(class_type="ORDINAL", process_func=process_ordinal_1, filter_func=filter_ordinal_1)
|
||||
)
|
||||
filters.append(
|
||||
Filter(class_type="DECIMAL", process_func=process_decimal_1, filter_func=filter_decimal_1)
|
||||
)
|
||||
filters.append(
|
||||
Filter(class_type="MEASURE", process_func=process_measure_1, filter_func=filter_measure_1)
|
||||
)
|
||||
filters.append(Filter(class_type="MONEY", process_func=process_money_1, filter_func=filter_money_1))
|
||||
filters.append(Filter(class_type="TIME", process_func=process_time_1, filter_func=filter_time_1))
|
||||
|
||||
filters.append(Filter(class_type="DATE", process_func=process_date_1, filter_func=filter_date_1))
|
||||
filters.append(Filter(class_type="PLAIN", process_func=process_plain_1, filter_func=filter_plain_1))
|
||||
filters.append(Filter(class_type="PUNCT", process_func=process_punct_1, filter_func=filter_punct_1))
|
||||
filters.append(
|
||||
Filter(class_type="LETTERS", process_func=process_letters_1, filter_func=filter_letters_1)
|
||||
)
|
||||
filters.append(
|
||||
Filter(class_type="VERBATIM", process_func=process_verbatim_1, filter_func=filter_verbatim_1)
|
||||
)
|
||||
filters.append(Filter(class_type="DIGIT", process_func=process_digit_1, filter_func=filter_digit_1))
|
||||
filters.append(
|
||||
Filter(class_type="TELEPHONE", process_func=process_telephone_1, filter_func=filter_telephone_1)
|
||||
)
|
||||
filters.append(
|
||||
Filter(
|
||||
class_type="ELECTRONIC", process_func=process_electronic_1, filter_func=filter_electronic_1
|
||||
)
|
||||
)
|
||||
filters.append(
|
||||
Filter(class_type="FRACTION", process_func=process_fraction_1, filter_func=filter_fraction_1)
|
||||
)
|
||||
filters.append(
|
||||
Filter(class_type="ADDRESS", process_func=process_address_1, filter_func=filter_address_1)
|
||||
)
|
||||
filters.append(Filter(class_type=EOS_TYPE, process_func=lambda x: x, filter_func=lambda x: True))
|
||||
|
||||
|
||||
def filter_loaded_data(data: List[Instance], verbose: bool = False) -> List[Instance]:
|
||||
"""
|
||||
Filters list of instances
|
||||
|
||||
Args:
|
||||
data: list of instances
|
||||
|
||||
Returns: filtered and transformed list of instances
|
||||
"""
|
||||
updates_instances = []
|
||||
for instance in data:
|
||||
updated_instance = False
|
||||
for fil in filters:
|
||||
if fil.class_type == instance.token_type and fil.filter(instance):
|
||||
instance = fil.process(instance)
|
||||
updated_instance = True
|
||||
if updated_instance:
|
||||
if verbose:
|
||||
print(instance)
|
||||
updates_instances.append(instance)
|
||||
return updates_instances
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--input", help="input file path", type=str, default="./en_with_types/output-00001-of-00100"
|
||||
)
|
||||
parser.add_argument("--verbose", help="print filtered instances", action="store_true")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = parse_args()
|
||||
file_path = args.input
|
||||
|
||||
print("Loading training data: " + file_path)
|
||||
instance_list = load_files([file_path]) # List of instances
|
||||
filtered_instance_list = filter_loaded_data(instance_list, args.verbose)
|
||||
training_data_to_sentences(filtered_instance_list)
|
||||
@@ -0,0 +1,35 @@
|
||||
$ dollar
|
||||
$ us dollar
|
||||
$ united states dollar
|
||||
£ british pound
|
||||
€ euro
|
||||
₩ won
|
||||
nzd new zealand dollar
|
||||
rs rupee
|
||||
chf swiss franc
|
||||
dkk danish kroner
|
||||
fim finnish markka
|
||||
aed arab emirates dirham
|
||||
¥ yen
|
||||
czk czech koruna
|
||||
mro mauritanian ouguiya
|
||||
pkr pakistani rupee
|
||||
crc costa rican colon
|
||||
hk$ hong kong dollar
|
||||
npr nepalese rupee
|
||||
awg aruban florin
|
||||
nok norwegian kroner
|
||||
tzs tanzanian shilling
|
||||
sek swedish kronor
|
||||
cyp cypriot pound
|
||||
r real
|
||||
sar saudi riyal
|
||||
cve cape verde escudo
|
||||
rsd serbian dinar
|
||||
dm german mark
|
||||
shp saint helena pounds
|
||||
php philippine peso
|
||||
cad canadian dollar
|
||||
ssp south sudanese pound
|
||||
scr seychelles rupee
|
||||
mvr maldivian rufiyaa
|
||||
|
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
com
|
||||
uk
|
||||
fr
|
||||
net
|
||||
br
|
||||
in
|
||||
ru
|
||||
de
|
||||
it
|
||||
ai
|
||||
|
@@ -0,0 +1,17 @@
|
||||
g mail gmail
|
||||
gmail
|
||||
n vidia nvidia
|
||||
nvidia
|
||||
outlook
|
||||
hotmail
|
||||
yahoo
|
||||
aol
|
||||
gmx
|
||||
msn
|
||||
live
|
||||
yandex
|
||||
orange
|
||||
wanadoo
|
||||
web
|
||||
comcast
|
||||
google
|
||||
|
@@ -0,0 +1,22 @@
|
||||
. dot
|
||||
- dash
|
||||
- hyphen
|
||||
_ underscore
|
||||
! exclamation mark
|
||||
# number sign
|
||||
$ dollar sign
|
||||
% percent sign
|
||||
& ampersand
|
||||
' quote
|
||||
* asterisk
|
||||
+ plus
|
||||
/ slash
|
||||
= equal sign
|
||||
? question mark
|
||||
^ circumflex
|
||||
` right single quote
|
||||
{ left brace
|
||||
| vertical bar
|
||||
} right brace
|
||||
~ tilde
|
||||
, comma
|
||||
|
@@ -0,0 +1,4 @@
|
||||
k thousand
|
||||
m million
|
||||
b billion
|
||||
t trillion
|
||||
|
@@ -0,0 +1,109 @@
|
||||
f fahrenheit
|
||||
c celsius
|
||||
km kilometer
|
||||
m meter
|
||||
cm centimeter
|
||||
mm millimeter
|
||||
ha hectare
|
||||
mi mile
|
||||
m² square meter
|
||||
km² square kilometer
|
||||
ft foot
|
||||
% percent
|
||||
hz hertz
|
||||
kw kilowatt
|
||||
hp horsepower
|
||||
mg milligram
|
||||
kg kilogram
|
||||
ghz gigahertz
|
||||
khz kilohertz
|
||||
mhz megahertz
|
||||
v volt
|
||||
h hour
|
||||
mc mega coulomb
|
||||
s second
|
||||
nm nanometer
|
||||
rpm revolution per minute
|
||||
min minute
|
||||
mA milli ampere
|
||||
% per cent
|
||||
kwh kilo watt hour
|
||||
m³ cubic meter
|
||||
mph mile per hour
|
||||
tw tera watt
|
||||
mv milli volt
|
||||
mw megawatt
|
||||
μm micrometer
|
||||
" inch
|
||||
tb terabyte
|
||||
cc c c
|
||||
g gram
|
||||
da dalton
|
||||
atm atmosphere
|
||||
ω ohm
|
||||
db decibel
|
||||
ps peta second
|
||||
oz ounce
|
||||
hl hecto liter
|
||||
μg microgram
|
||||
pg petagram
|
||||
gb gigabyte
|
||||
kb kilobit
|
||||
ev electron volt
|
||||
mb megabyte
|
||||
kb kilobyte
|
||||
kbps kilobit per second
|
||||
mbps megabit per second
|
||||
st stone
|
||||
kl kilo liter
|
||||
tj tera joule
|
||||
kv kilo volt
|
||||
mv mega volt
|
||||
kn kilonewton
|
||||
mm megameter
|
||||
au astronomical unit
|
||||
yd yard
|
||||
rad radian
|
||||
lm lumen
|
||||
hs hecto second
|
||||
mol mole
|
||||
gpa giga pascal
|
||||
ml milliliter
|
||||
gw gigawatt
|
||||
ma mega ampere
|
||||
kt knot
|
||||
kgf kilogram force
|
||||
ng nano gram
|
||||
ns nanosecond
|
||||
ms mega siemens
|
||||
bar bar
|
||||
gl giga liter
|
||||
μs microsecond
|
||||
da deci ampere
|
||||
pa pascal
|
||||
ds deci second
|
||||
ms milli second
|
||||
dm deci meter
|
||||
dm³ cubic deci meter
|
||||
amu atomic mass unit
|
||||
mb megabit
|
||||
mf mega farad
|
||||
bq becquerel
|
||||
pb petabit
|
||||
mm² square millimeter
|
||||
cm² square centimeter
|
||||
sq mi square mile
|
||||
sq ft square foot
|
||||
kpa kilopascal
|
||||
cd candela
|
||||
tl tera liter
|
||||
ms mega second
|
||||
mpa megapascal
|
||||
pm peta meter
|
||||
pb peta byte
|
||||
gwh giga watt hour
|
||||
kcal kilo calory
|
||||
gy gray
|
||||
sv sievert
|
||||
cwt hundredweight
|
||||
cc c c
|
||||
|
Can't render this file because it contains an unexpected character in line 109 and column 8.
|
@@ -0,0 +1,12 @@
|
||||
january
|
||||
february
|
||||
march
|
||||
april
|
||||
may
|
||||
june
|
||||
july
|
||||
august
|
||||
september
|
||||
october
|
||||
november
|
||||
december
|
||||
|
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
one 1
|
||||
two 2
|
||||
three 3
|
||||
four 4
|
||||
five 5
|
||||
six 6
|
||||
seven 7
|
||||
eight 8
|
||||
nine 9
|
||||
|
@@ -0,0 +1 @@
|
||||
hundred
|
||||
|
@@ -0,0 +1,10 @@
|
||||
ten 10
|
||||
eleven 11
|
||||
twelve 12
|
||||
thirteen 13
|
||||
fourteen 14
|
||||
fifteen 15
|
||||
sixteen 16
|
||||
seventeen 17
|
||||
eighteen 18
|
||||
nineteen 19
|
||||
|
@@ -0,0 +1,22 @@
|
||||
thousand
|
||||
million
|
||||
billion
|
||||
trillion
|
||||
quadrillion
|
||||
quintillion
|
||||
sextillion
|
||||
septillion
|
||||
octillion
|
||||
nonillion
|
||||
decillion
|
||||
undecillion
|
||||
duodecillion
|
||||
tredecillion
|
||||
quattuordecillion
|
||||
quindecillion
|
||||
sexdecillion
|
||||
septendecillion
|
||||
octodecillion
|
||||
novemdecillion
|
||||
vigintillion
|
||||
centillion
|
||||
|
@@ -0,0 +1,9 @@
|
||||
twenty 2
|
||||
thirty 3
|
||||
forty 4
|
||||
fourty 4
|
||||
fifty 5
|
||||
sixty 6
|
||||
seventy 7
|
||||
eighty 8
|
||||
ninety 9
|
||||
|
@@ -0,0 +1 @@
|
||||
zero 0
|
||||
|
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
first one
|
||||
second two
|
||||
third three
|
||||
fourth four
|
||||
fifth five
|
||||
sixth sixth
|
||||
seventh seven
|
||||
eighth eight
|
||||
ninth nine
|
||||
|
@@ -0,0 +1 @@
|
||||
twelfth twelve
|
||||
|
@@ -0,0 +1,59 @@
|
||||
1 59
|
||||
2 58
|
||||
3 57
|
||||
4 56
|
||||
5 55
|
||||
6 54
|
||||
7 53
|
||||
8 52
|
||||
9 51
|
||||
10 50
|
||||
11 49
|
||||
12 48
|
||||
13 47
|
||||
14 46
|
||||
15 45
|
||||
16 44
|
||||
17 43
|
||||
18 42
|
||||
19 41
|
||||
20 40
|
||||
21 39
|
||||
22 38
|
||||
23 37
|
||||
24 36
|
||||
25 35
|
||||
26 34
|
||||
27 33
|
||||
28 32
|
||||
29 31
|
||||
30 30
|
||||
31 29
|
||||
32 28
|
||||
33 27
|
||||
34 26
|
||||
35 25
|
||||
36 24
|
||||
37 23
|
||||
38 22
|
||||
39 21
|
||||
40 20
|
||||
41 19
|
||||
42 18
|
||||
43 17
|
||||
44 16
|
||||
45 15
|
||||
46 14
|
||||
47 13
|
||||
48 12
|
||||
49 11
|
||||
50 10
|
||||
51 9
|
||||
52 8
|
||||
53 7
|
||||
54 6
|
||||
55 5
|
||||
56 4
|
||||
57 3
|
||||
58 2
|
||||
59 1
|
||||
|
@@ -0,0 +1,8 @@
|
||||
p m p.m.
|
||||
pm p.m.
|
||||
p.m.
|
||||
p.m p.m.
|
||||
am a.m.
|
||||
a.m.
|
||||
a.m a.m.
|
||||
a m a.m.
|
||||
|
@@ -0,0 +1,7 @@
|
||||
cst c s t
|
||||
cet c e t
|
||||
pst p s t
|
||||
est e s t
|
||||
pt p t
|
||||
et e t
|
||||
gmt g m t
|
||||
|
@@ -0,0 +1,12 @@
|
||||
one 12
|
||||
two 1
|
||||
three 2
|
||||
four 3
|
||||
five 4
|
||||
six 5
|
||||
seven 6
|
||||
eigh 7
|
||||
nine 8
|
||||
ten 9
|
||||
eleven 10
|
||||
twelve 11
|
||||
|
@@ -0,0 +1,12 @@
|
||||
e.g. for example
|
||||
dr. doctor
|
||||
mr. mister
|
||||
mrs. misses
|
||||
st. saint
|
||||
7-eleven seven eleven
|
||||
es3 e s three
|
||||
s&p s and p
|
||||
ASAP a s a p
|
||||
AT&T a t and t
|
||||
LLP l l p
|
||||
ATM a t m
|
||||
|
@@ -0,0 +1,138 @@
|
||||
import pynini
|
||||
from fun_text_processing.inverse_text_normalization.en.utils import get_abs_path, num_to_word
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
DAMO_ALPHA,
|
||||
DAMO_DIGIT,
|
||||
DAMO_SIGMA,
|
||||
DAMO_SPACE,
|
||||
GraphFst,
|
||||
delete_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class CardinalFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying cardinals
|
||||
e.g. minus twenty three -> cardinal { integer: "23" negative: "-" } }
|
||||
Numbers below thirteen are not converted.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(name="cardinal", kind="classify")
|
||||
graph_zero = pynini.string_file(get_abs_path("data/numbers/zero.tsv"))
|
||||
graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv"))
|
||||
graph_ties = pynini.string_file(get_abs_path("data/numbers/ties.tsv"))
|
||||
graph_teen = pynini.string_file(get_abs_path("data/numbers/teen.tsv"))
|
||||
|
||||
graph_hundred = pynini.cross("hundred", "")
|
||||
|
||||
graph_hundred_component = pynini.union(
|
||||
graph_digit + delete_space + graph_hundred, pynutil.insert("0")
|
||||
)
|
||||
graph_hundred_component += delete_space
|
||||
graph_hundred_component += pynini.union(
|
||||
graph_teen | pynutil.insert("00"),
|
||||
(graph_ties | pynutil.insert("0")) + delete_space + (graph_digit | pynutil.insert("0")),
|
||||
)
|
||||
|
||||
graph_hundred_component_at_least_one_none_zero_digit = graph_hundred_component @ (
|
||||
pynini.closure(DAMO_DIGIT) + (DAMO_DIGIT - "0") + pynini.closure(DAMO_DIGIT)
|
||||
)
|
||||
self.graph_hundred_component_at_least_one_none_zero_digit = (
|
||||
graph_hundred_component_at_least_one_none_zero_digit
|
||||
)
|
||||
|
||||
graph_thousands = pynini.union(
|
||||
graph_hundred_component_at_least_one_none_zero_digit
|
||||
+ delete_space
|
||||
+ pynutil.delete("thousand"),
|
||||
pynutil.insert("000", weight=0.1),
|
||||
)
|
||||
|
||||
graph_million = pynini.union(
|
||||
graph_hundred_component_at_least_one_none_zero_digit
|
||||
+ delete_space
|
||||
+ pynutil.delete("million"),
|
||||
pynutil.insert("000", weight=0.1),
|
||||
)
|
||||
graph_billion = pynini.union(
|
||||
graph_hundred_component_at_least_one_none_zero_digit
|
||||
+ delete_space
|
||||
+ pynutil.delete("billion"),
|
||||
pynutil.insert("000", weight=0.1),
|
||||
)
|
||||
graph_trillion = pynini.union(
|
||||
graph_hundred_component_at_least_one_none_zero_digit
|
||||
+ delete_space
|
||||
+ pynutil.delete("trillion"),
|
||||
pynutil.insert("000", weight=0.1),
|
||||
)
|
||||
graph_quadrillion = pynini.union(
|
||||
graph_hundred_component_at_least_one_none_zero_digit
|
||||
+ delete_space
|
||||
+ pynutil.delete("quadrillion"),
|
||||
pynutil.insert("000", weight=0.1),
|
||||
)
|
||||
graph_quintillion = pynini.union(
|
||||
graph_hundred_component_at_least_one_none_zero_digit
|
||||
+ delete_space
|
||||
+ pynutil.delete("quintillion"),
|
||||
pynutil.insert("000", weight=0.1),
|
||||
)
|
||||
graph_sextillion = pynini.union(
|
||||
graph_hundred_component_at_least_one_none_zero_digit
|
||||
+ delete_space
|
||||
+ pynutil.delete("sextillion"),
|
||||
pynutil.insert("000", weight=0.1),
|
||||
)
|
||||
|
||||
graph = pynini.union(
|
||||
graph_sextillion
|
||||
+ delete_space
|
||||
+ graph_quintillion
|
||||
+ delete_space
|
||||
+ graph_quadrillion
|
||||
+ delete_space
|
||||
+ graph_trillion
|
||||
+ delete_space
|
||||
+ graph_billion
|
||||
+ delete_space
|
||||
+ graph_million
|
||||
+ delete_space
|
||||
+ graph_thousands
|
||||
+ delete_space
|
||||
+ graph_hundred_component,
|
||||
graph_zero,
|
||||
)
|
||||
|
||||
graph = graph @ pynini.union(
|
||||
pynutil.delete(pynini.closure("0"))
|
||||
+ pynini.difference(DAMO_DIGIT, "0")
|
||||
+ pynini.closure(DAMO_DIGIT),
|
||||
"0",
|
||||
)
|
||||
|
||||
labels_exception = [num_to_word(x) for x in range(0, 13)]
|
||||
graph_exception = pynini.union(*labels_exception)
|
||||
|
||||
graph = (
|
||||
pynini.cdrewrite(pynutil.delete("and"), DAMO_SPACE, DAMO_SPACE, DAMO_SIGMA)
|
||||
@ (DAMO_ALPHA + DAMO_SIGMA)
|
||||
@ graph
|
||||
)
|
||||
|
||||
self.graph_no_exception = graph
|
||||
|
||||
self.graph = (pynini.project(graph, "input") - graph_exception.arcsort()) @ graph
|
||||
|
||||
optional_minus_graph = pynini.closure(
|
||||
pynutil.insert("negative: ") + pynini.cross("minus", '"-"') + DAMO_SPACE, 0, 1
|
||||
)
|
||||
|
||||
final_graph = (
|
||||
optional_minus_graph + pynutil.insert('integer: "') + self.graph + pynutil.insert('"')
|
||||
)
|
||||
|
||||
final_graph = self.add_tokens(final_graph)
|
||||
self.fst = final_graph.optimize()
|
||||
@@ -0,0 +1,150 @@
|
||||
import pynini
|
||||
from fun_text_processing.inverse_text_normalization.en.utils import get_abs_path
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
DAMO_ALPHA,
|
||||
DAMO_DIGIT,
|
||||
GraphFst,
|
||||
delete_extra_space,
|
||||
delete_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
graph_teen = pynini.string_file(get_abs_path("data/numbers/teen.tsv")).optimize()
|
||||
graph_digit = pynini.string_file(get_abs_path("data/numbers/digit.tsv")).optimize()
|
||||
ties_graph = pynini.string_file(get_abs_path("data/numbers/ties.tsv")).optimize()
|
||||
|
||||
|
||||
def _get_month_graph():
|
||||
"""
|
||||
Transducer for month, e.g. march -> march
|
||||
"""
|
||||
month_graph = pynini.string_file(get_abs_path("data/months.tsv"))
|
||||
return month_graph
|
||||
|
||||
|
||||
def _get_ties_graph():
|
||||
"""
|
||||
Transducer for 20-99 e.g
|
||||
twenty three -> 23
|
||||
"""
|
||||
graph = ties_graph + (delete_space + graph_digit | pynutil.insert("0"))
|
||||
return graph
|
||||
|
||||
|
||||
def _get_range_graph():
|
||||
"""
|
||||
Transducer for decades (1**0s, 2**0s), centuries (2*00s, 1*00s), millennia (2000s)
|
||||
"""
|
||||
graph_ties = _get_ties_graph()
|
||||
graph = (graph_ties | graph_teen) + delete_space + pynini.cross("hundreds", "00s")
|
||||
graph |= pynini.cross("two", "2") + delete_space + pynini.cross("thousands", "000s")
|
||||
graph |= (
|
||||
(graph_ties | graph_teen)
|
||||
+ delete_space
|
||||
+ (pynini.closure(DAMO_ALPHA, 1) + (pynini.cross("ies", "y") | pynutil.delete("s")))
|
||||
@ (graph_ties | pynini.cross("ten", "10"))
|
||||
+ pynutil.insert("s")
|
||||
)
|
||||
graph @= pynini.union("1", "2") + DAMO_DIGIT + DAMO_DIGIT + DAMO_DIGIT + "s"
|
||||
return graph
|
||||
|
||||
|
||||
def _get_year_graph():
|
||||
"""
|
||||
Transducer for year, e.g. twenty twenty -> 2020
|
||||
"""
|
||||
|
||||
def _get_digits_graph():
|
||||
zero = pynini.cross((pynini.accep("oh") | pynini.accep("o")), "0")
|
||||
graph = zero + delete_space + graph_digit
|
||||
graph.optimize()
|
||||
return graph
|
||||
|
||||
def _get_thousands_graph():
|
||||
graph_ties = _get_ties_graph()
|
||||
graph_hundred_component = (
|
||||
graph_digit + delete_space + pynutil.delete("hundred")
|
||||
) | pynutil.insert("0")
|
||||
graph = (
|
||||
graph_digit
|
||||
+ delete_space
|
||||
+ pynutil.delete("thousand")
|
||||
+ delete_space
|
||||
+ graph_hundred_component
|
||||
+ delete_space
|
||||
+ (graph_teen | graph_ties)
|
||||
)
|
||||
return graph
|
||||
|
||||
graph_ties = _get_ties_graph()
|
||||
graph_digits = _get_digits_graph()
|
||||
graph_thousands = _get_thousands_graph()
|
||||
year_graph = (
|
||||
# 20 19, 40 12, 2012 - assuming no limit on the year
|
||||
(graph_teen + delete_space + (graph_ties | graph_digits | graph_teen))
|
||||
| (graph_ties + delete_space + (graph_ties | graph_digits | graph_teen))
|
||||
| graph_thousands
|
||||
)
|
||||
year_graph.optimize()
|
||||
return year_graph
|
||||
|
||||
|
||||
class DateFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying date,
|
||||
e.g. january fifth twenty twelve -> date { month: "january" day: "5" year: "2012" preserve_order: true }
|
||||
e.g. the fifth of january twenty twelve -> date { day: "5" month: "january" year: "2012" preserve_order: true }
|
||||
e.g. twenty twenty -> date { year: "2012" preserve_order: true }
|
||||
|
||||
Args:
|
||||
ordinal: OrdinalFst
|
||||
"""
|
||||
|
||||
def __init__(self, ordinal: GraphFst):
|
||||
super().__init__(name="date", kind="classify")
|
||||
|
||||
ordinal_graph = ordinal.graph
|
||||
year_graph = _get_year_graph()
|
||||
YEAR_WEIGHT = 0.001
|
||||
year_graph = pynutil.add_weight(year_graph, YEAR_WEIGHT)
|
||||
month_graph = _get_month_graph()
|
||||
|
||||
month_graph = pynutil.insert('month: "') + month_graph + pynutil.insert('"')
|
||||
|
||||
day_graph = (
|
||||
pynutil.insert('day: "') + pynutil.add_weight(ordinal_graph, -0.7) + pynutil.insert('"')
|
||||
)
|
||||
graph_year = (
|
||||
delete_extra_space
|
||||
+ pynutil.insert('year: "')
|
||||
+ pynutil.add_weight(year_graph, -YEAR_WEIGHT)
|
||||
+ pynutil.insert('"')
|
||||
)
|
||||
optional_graph_year = pynini.closure(
|
||||
graph_year,
|
||||
0,
|
||||
1,
|
||||
)
|
||||
graph_mdy = month_graph + (
|
||||
(delete_extra_space + day_graph)
|
||||
| graph_year
|
||||
| (delete_extra_space + day_graph + graph_year)
|
||||
)
|
||||
graph_dmy = (
|
||||
pynutil.delete("the")
|
||||
+ delete_space
|
||||
+ day_graph
|
||||
+ delete_space
|
||||
+ pynutil.delete("of")
|
||||
+ delete_extra_space
|
||||
+ month_graph
|
||||
+ optional_graph_year
|
||||
)
|
||||
graph_year = (
|
||||
pynutil.insert('year: "') + (year_graph | _get_range_graph()) + pynutil.insert('"')
|
||||
)
|
||||
|
||||
final_graph = graph_mdy | graph_dmy | graph_year
|
||||
final_graph += pynutil.insert(" preserve_order: true")
|
||||
final_graph = self.add_tokens(final_graph)
|
||||
self.fst = final_graph.optimize()
|
||||
@@ -0,0 +1,100 @@
|
||||
import pynini
|
||||
from fun_text_processing.inverse_text_normalization.en.utils import get_abs_path
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
DAMO_DIGIT,
|
||||
GraphFst,
|
||||
delete_extra_space,
|
||||
delete_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
def get_quantity(
|
||||
decimal: "pynini.FstLike", cardinal_up_to_hundred: "pynini.FstLike"
|
||||
) -> "pynini.FstLike":
|
||||
"""
|
||||
Returns FST that transforms either a cardinal or decimal followed by a quantity into a numeral,
|
||||
e.g. one million -> integer_part: "1" quantity: "million"
|
||||
e.g. one point five million -> integer_part: "1" fractional_part: "5" quantity: "million"
|
||||
|
||||
Args:
|
||||
decimal: decimal FST
|
||||
cardinal_up_to_hundred: cardinal FST
|
||||
"""
|
||||
numbers = cardinal_up_to_hundred @ (
|
||||
pynutil.delete(pynini.closure("0"))
|
||||
+ pynini.difference(DAMO_DIGIT, "0")
|
||||
+ pynini.closure(DAMO_DIGIT)
|
||||
)
|
||||
suffix = pynini.union(
|
||||
"million", "billion", "trillion", "quadrillion", "quintillion", "sextillion"
|
||||
)
|
||||
res = (
|
||||
pynutil.insert('integer_part: "')
|
||||
+ numbers
|
||||
+ pynutil.insert('"')
|
||||
+ delete_extra_space
|
||||
+ pynutil.insert('quantity: "')
|
||||
+ suffix
|
||||
+ pynutil.insert('"')
|
||||
)
|
||||
res |= (
|
||||
decimal
|
||||
+ delete_extra_space
|
||||
+ pynutil.insert('quantity: "')
|
||||
+ (suffix | "thousand")
|
||||
+ pynutil.insert('"')
|
||||
)
|
||||
return res
|
||||
|
||||
|
||||
class DecimalFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying decimal
|
||||
e.g. minus twelve point five o o six billion -> decimal { negative: "true" integer_part: "12" fractional_part: "5006" quantity: "billion" }
|
||||
e.g. one billion -> decimal { integer_part: "1" quantity: "billion" }
|
||||
Args:
|
||||
cardinal: CardinalFst
|
||||
"""
|
||||
|
||||
def __init__(self, cardinal: GraphFst):
|
||||
super().__init__(name="decimal", kind="classify")
|
||||
|
||||
cardinal_graph = cardinal.graph_no_exception
|
||||
|
||||
graph_decimal = pynini.string_file(get_abs_path("data/numbers/digit.tsv"))
|
||||
graph_decimal |= pynini.string_file(get_abs_path("data/numbers/zero.tsv")) | pynini.cross(
|
||||
"o", "0"
|
||||
)
|
||||
|
||||
graph_decimal = pynini.closure(graph_decimal + delete_space) + graph_decimal
|
||||
self.graph = graph_decimal
|
||||
|
||||
point = pynutil.delete("point")
|
||||
|
||||
optional_graph_negative = pynini.closure(
|
||||
pynutil.insert("negative: ") + pynini.cross("minus", '"true"') + delete_extra_space,
|
||||
0,
|
||||
1,
|
||||
)
|
||||
|
||||
graph_fractional = (
|
||||
pynutil.insert('fractional_part: "') + graph_decimal + pynutil.insert('"')
|
||||
)
|
||||
graph_integer = pynutil.insert('integer_part: "') + cardinal_graph + pynutil.insert('"')
|
||||
final_graph_wo_sign = (
|
||||
pynini.closure(graph_integer + delete_extra_space, 0, 1)
|
||||
+ point
|
||||
+ delete_extra_space
|
||||
+ graph_fractional
|
||||
)
|
||||
final_graph = optional_graph_negative + final_graph_wo_sign
|
||||
|
||||
self.final_graph_wo_negative = final_graph_wo_sign | get_quantity(
|
||||
final_graph_wo_sign, cardinal.graph_hundred_component_at_least_one_none_zero_digit
|
||||
)
|
||||
final_graph |= optional_graph_negative + get_quantity(
|
||||
final_graph_wo_sign, cardinal.graph_hundred_component_at_least_one_none_zero_digit
|
||||
)
|
||||
final_graph = self.add_tokens(final_graph)
|
||||
self.fst = final_graph.optimize()
|
||||
@@ -0,0 +1,97 @@
|
||||
import pynini
|
||||
from fun_text_processing.inverse_text_normalization.en.utils import get_abs_path
|
||||
from fun_text_processing.text_normalization.en.graph_utils import DAMO_ALPHA, GraphFst, insert_space
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class ElectronicFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying electronic: as URLs, email addresses, etc.
|
||||
e.g. c d f one at a b c dot e d u -> tokens { electronic { username: "cdf1" domain: "abc.edu" } }
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(name="electronic", kind="classify")
|
||||
|
||||
delete_extra_space = pynutil.delete(" ")
|
||||
alpha_num = (
|
||||
DAMO_ALPHA
|
||||
| pynini.string_file(get_abs_path("data/numbers/digit.tsv"))
|
||||
| pynini.string_file(get_abs_path("data/numbers/zero.tsv"))
|
||||
)
|
||||
|
||||
symbols = pynini.string_file(get_abs_path("data/electronic/symbols.tsv")).invert()
|
||||
|
||||
accepted_username = alpha_num | symbols
|
||||
process_dot = pynini.cross("dot", ".")
|
||||
username = (
|
||||
alpha_num + pynini.closure(delete_extra_space + accepted_username)
|
||||
) | pynutil.add_weight(pynini.closure(DAMO_ALPHA, 1), weight=0.0001)
|
||||
username = pynutil.insert('username: "') + username + pynutil.insert('"')
|
||||
single_alphanum = pynini.closure(alpha_num + delete_extra_space) + alpha_num
|
||||
server = single_alphanum | pynini.string_file(
|
||||
get_abs_path("data/electronic/server_name.tsv")
|
||||
)
|
||||
domain = single_alphanum | pynini.string_file(get_abs_path("data/electronic/domain.tsv"))
|
||||
domain_graph = (
|
||||
pynutil.insert('domain: "')
|
||||
+ server
|
||||
+ delete_extra_space
|
||||
+ process_dot
|
||||
+ delete_extra_space
|
||||
+ domain
|
||||
+ pynutil.insert('"')
|
||||
)
|
||||
graph = (
|
||||
username
|
||||
+ delete_extra_space
|
||||
+ pynutil.delete("at")
|
||||
+ insert_space
|
||||
+ delete_extra_space
|
||||
+ domain_graph
|
||||
)
|
||||
|
||||
############# url ###
|
||||
protocol_end = pynini.cross(pynini.union("w w w", "www"), "www")
|
||||
protocol_start = (
|
||||
pynini.cross("h t t p", "http") | pynini.cross("h t t p s", "https")
|
||||
) + pynini.cross(" colon slash slash ", "://")
|
||||
# .com,
|
||||
ending = (
|
||||
delete_extra_space
|
||||
+ symbols
|
||||
+ delete_extra_space
|
||||
+ (
|
||||
domain
|
||||
| pynini.closure(
|
||||
accepted_username + delete_extra_space,
|
||||
)
|
||||
+ accepted_username
|
||||
)
|
||||
)
|
||||
|
||||
protocol_default = (
|
||||
(
|
||||
(pynini.closure(delete_extra_space + accepted_username, 1) | server)
|
||||
| pynutil.add_weight(pynini.closure(DAMO_ALPHA, 1), weight=0.0001)
|
||||
)
|
||||
+ pynini.closure(ending, 1)
|
||||
).optimize()
|
||||
protocol = (
|
||||
pynini.closure(protocol_start, 0, 1)
|
||||
+ protocol_end
|
||||
+ delete_extra_space
|
||||
+ process_dot
|
||||
+ protocol_default
|
||||
).optimize()
|
||||
|
||||
protocol |= (
|
||||
pynini.closure(protocol_end + delete_extra_space + process_dot, 0, 1) + protocol_default
|
||||
)
|
||||
|
||||
protocol = pynutil.insert('protocol: "') + protocol.optimize() + pynutil.insert('"')
|
||||
graph |= protocol
|
||||
########
|
||||
|
||||
final_graph = self.add_tokens(graph)
|
||||
self.fst = final_graph.optimize()
|
||||
@@ -0,0 +1,11 @@
|
||||
from fun_text_processing.text_normalization.en.graph_utils import GraphFst
|
||||
|
||||
|
||||
class FractionFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying fraction
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(name="fraction", kind="classify")
|
||||
# integer_part # numerator # denominator
|
||||
@@ -0,0 +1,97 @@
|
||||
import pynini
|
||||
from fun_text_processing.inverse_text_normalization.en.utils import get_abs_path
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
DAMO_SIGMA,
|
||||
GraphFst,
|
||||
convert_space,
|
||||
delete_extra_space,
|
||||
delete_space,
|
||||
get_singulars,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class MeasureFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying measure
|
||||
e.g. minus twelve kilograms -> measure { negative: "true" cardinal { integer: "12" } units: "kg" }
|
||||
|
||||
Args:
|
||||
cardinal: CardinalFst
|
||||
decimal: DecimalFst
|
||||
"""
|
||||
|
||||
def __init__(self, cardinal: GraphFst, decimal: GraphFst):
|
||||
super().__init__(name="measure", kind="classify")
|
||||
|
||||
cardinal_graph = cardinal.graph_no_exception
|
||||
|
||||
graph_unit = pynini.string_file(get_abs_path("data/measurements.tsv"))
|
||||
graph_unit_singular = pynini.invert(graph_unit) # singular -> abbr
|
||||
graph_unit_plural = get_singulars(graph_unit_singular) # plural -> abbr
|
||||
|
||||
optional_graph_negative = pynini.closure(
|
||||
pynutil.insert("negative: ") + pynini.cross("minus", '"true"') + delete_extra_space,
|
||||
0,
|
||||
1,
|
||||
)
|
||||
|
||||
unit_singular = convert_space(graph_unit_singular)
|
||||
unit_plural = convert_space(graph_unit_plural)
|
||||
unit_misc = (
|
||||
pynutil.insert("/")
|
||||
+ pynutil.delete("per")
|
||||
+ delete_space
|
||||
+ convert_space(graph_unit_singular)
|
||||
)
|
||||
|
||||
unit_singular = (
|
||||
pynutil.insert('units: "')
|
||||
+ (
|
||||
unit_singular
|
||||
| unit_misc
|
||||
| pynutil.add_weight(unit_singular + delete_space + unit_misc, 0.01)
|
||||
)
|
||||
+ pynutil.insert('"')
|
||||
)
|
||||
unit_plural = (
|
||||
pynutil.insert('units: "')
|
||||
+ (
|
||||
unit_plural
|
||||
| unit_misc
|
||||
| pynutil.add_weight(unit_plural + delete_space + unit_misc, 0.01)
|
||||
)
|
||||
+ pynutil.insert('"')
|
||||
)
|
||||
|
||||
subgraph_decimal = (
|
||||
pynutil.insert("decimal { ")
|
||||
+ optional_graph_negative
|
||||
+ decimal.final_graph_wo_negative
|
||||
+ pynutil.insert(" }")
|
||||
+ delete_extra_space
|
||||
+ unit_plural
|
||||
)
|
||||
subgraph_cardinal = (
|
||||
pynutil.insert("cardinal { ")
|
||||
+ optional_graph_negative
|
||||
+ pynutil.insert('integer: "')
|
||||
+ ((DAMO_SIGMA - "one") @ cardinal_graph)
|
||||
+ pynutil.insert('"')
|
||||
+ pynutil.insert(" }")
|
||||
+ delete_extra_space
|
||||
+ unit_plural
|
||||
)
|
||||
subgraph_cardinal |= (
|
||||
pynutil.insert("cardinal { ")
|
||||
+ optional_graph_negative
|
||||
+ pynutil.insert('integer: "')
|
||||
+ pynini.cross("one", "1")
|
||||
+ pynutil.insert('"')
|
||||
+ pynutil.insert(" }")
|
||||
+ delete_extra_space
|
||||
+ unit_singular
|
||||
)
|
||||
final_graph = subgraph_decimal | subgraph_cardinal
|
||||
final_graph = self.add_tokens(final_graph)
|
||||
self.fst = final_graph.optimize()
|
||||
@@ -0,0 +1,110 @@
|
||||
import pynini
|
||||
from fun_text_processing.inverse_text_normalization.en.utils import get_abs_path
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
DAMO_DIGIT,
|
||||
DAMO_NOT_SPACE,
|
||||
DAMO_SIGMA,
|
||||
GraphFst,
|
||||
convert_space,
|
||||
delete_extra_space,
|
||||
delete_space,
|
||||
get_singulars,
|
||||
insert_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class MoneyFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying money
|
||||
e.g. twelve dollars and five cents -> money { integer_part: "12" fractional_part: 05 currency: "$" }
|
||||
|
||||
Args:
|
||||
cardinal: CardinalFst
|
||||
decimal: DecimalFst
|
||||
"""
|
||||
|
||||
def __init__(self, cardinal: GraphFst, decimal: GraphFst):
|
||||
super().__init__(name="money", kind="classify")
|
||||
# quantity, integer_part, fractional_part, currency
|
||||
|
||||
cardinal_graph = cardinal.graph_no_exception
|
||||
# add support for missing hundred (only for 3 digit numbers)
|
||||
# "one fifty" -> "one hundred fifty"
|
||||
with_hundred = pynini.compose(
|
||||
pynini.closure(DAMO_NOT_SPACE)
|
||||
+ pynini.accep(" ")
|
||||
+ pynutil.insert("hundred ")
|
||||
+ DAMO_SIGMA,
|
||||
pynini.compose(cardinal_graph, DAMO_DIGIT**3),
|
||||
)
|
||||
cardinal_graph |= with_hundred
|
||||
graph_decimal_final = decimal.final_graph_wo_negative
|
||||
|
||||
unit = pynini.string_file(get_abs_path("data/currency.tsv"))
|
||||
unit_singular = pynini.invert(unit)
|
||||
unit_plural = get_singulars(unit_singular)
|
||||
|
||||
graph_unit_singular = (
|
||||
pynutil.insert('currency: "') + convert_space(unit_singular) + pynutil.insert('"')
|
||||
)
|
||||
graph_unit_plural = (
|
||||
pynutil.insert('currency: "') + convert_space(unit_plural) + pynutil.insert('"')
|
||||
)
|
||||
|
||||
add_leading_zero_to_double_digit = (DAMO_DIGIT + DAMO_DIGIT) | (
|
||||
pynutil.insert("0") + DAMO_DIGIT
|
||||
)
|
||||
# twelve dollars (and) fifty cents, zero cents
|
||||
cents_standalone = (
|
||||
pynutil.insert('fractional_part: "')
|
||||
+ pynini.union(
|
||||
pynutil.add_weight(((DAMO_SIGMA - "one") @ cardinal_graph), -0.7)
|
||||
@ add_leading_zero_to_double_digit
|
||||
+ delete_space
|
||||
+ (pynutil.delete("cents") | pynutil.delete("cent")),
|
||||
pynini.cross("one", "01") + delete_space + pynutil.delete("cent"),
|
||||
)
|
||||
+ pynutil.insert('"')
|
||||
)
|
||||
|
||||
optional_cents_standalone = pynini.closure(
|
||||
delete_space
|
||||
+ pynini.closure(pynutil.delete("and") + delete_space, 0, 1)
|
||||
+ insert_space
|
||||
+ cents_standalone,
|
||||
0,
|
||||
1,
|
||||
)
|
||||
# twelve dollars fifty, only after integer
|
||||
optional_cents_suffix = pynini.closure(
|
||||
delete_extra_space
|
||||
+ pynutil.insert('fractional_part: "')
|
||||
+ pynutil.add_weight(cardinal_graph @ add_leading_zero_to_double_digit, -0.7)
|
||||
+ pynutil.insert('"'),
|
||||
0,
|
||||
1,
|
||||
)
|
||||
|
||||
graph_integer = (
|
||||
pynutil.insert('integer_part: "')
|
||||
+ ((DAMO_SIGMA - "one") @ cardinal_graph)
|
||||
+ pynutil.insert('"')
|
||||
+ delete_extra_space
|
||||
+ graph_unit_plural
|
||||
+ (optional_cents_standalone | optional_cents_suffix)
|
||||
)
|
||||
graph_integer |= (
|
||||
pynutil.insert('integer_part: "')
|
||||
+ pynini.cross("one", "1")
|
||||
+ pynutil.insert('"')
|
||||
+ delete_extra_space
|
||||
+ graph_unit_singular
|
||||
+ (optional_cents_standalone | optional_cents_suffix)
|
||||
)
|
||||
graph_decimal = graph_decimal_final + delete_extra_space + graph_unit_plural
|
||||
graph_decimal |= pynutil.insert('currency: "$" integer_part: "0" ') + cents_standalone
|
||||
final_graph = graph_integer | graph_decimal
|
||||
|
||||
final_graph = self.add_tokens(final_graph)
|
||||
self.fst = final_graph.optimize()
|
||||
@@ -0,0 +1,29 @@
|
||||
import pynini
|
||||
from fun_text_processing.inverse_text_normalization.en.utils import get_abs_path
|
||||
from fun_text_processing.text_normalization.en.graph_utils import DAMO_CHAR, GraphFst
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class OrdinalFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying ordinal
|
||||
e.g. thirteenth -> ordinal { integer: "13" }
|
||||
|
||||
Args:
|
||||
cardinal: CardinalFst
|
||||
"""
|
||||
|
||||
def __init__(self, cardinal: GraphFst):
|
||||
super().__init__(name="ordinal", kind="classify")
|
||||
|
||||
cardinal_graph = cardinal.graph_no_exception
|
||||
graph_digit = pynini.string_file(get_abs_path("data/ordinals/digit.tsv"))
|
||||
graph_teens = pynini.string_file(get_abs_path("data/ordinals/teen.tsv"))
|
||||
graph = pynini.closure(DAMO_CHAR) + pynini.union(
|
||||
graph_digit, graph_teens, pynini.cross("tieth", "ty"), pynini.cross("th", "")
|
||||
)
|
||||
|
||||
self.graph = graph @ cardinal_graph
|
||||
final_graph = pynutil.insert('integer: "') + self.graph + pynutil.insert('"')
|
||||
final_graph = self.add_tokens(final_graph)
|
||||
self.fst = final_graph.optimize()
|
||||
@@ -0,0 +1,20 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import GraphFst
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class PunctuationFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying punctuation
|
||||
e.g. a, -> tokens { name: "a" } tokens { name: "," }
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(name="punctuation", kind="classify")
|
||||
|
||||
s = "!#$%&'()*+,-./:;<=>?@^_`{|}~"
|
||||
punct = pynini.union(*s)
|
||||
|
||||
graph = pynutil.insert('name: "') + punct + pynutil.insert('"')
|
||||
|
||||
self.fst = graph.optimize()
|
||||
@@ -0,0 +1,149 @@
|
||||
import pynini
|
||||
from fun_text_processing.inverse_text_normalization.en.utils import get_abs_path
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
DAMO_ALNUM,
|
||||
DAMO_ALPHA,
|
||||
DAMO_DIGIT,
|
||||
GraphFst,
|
||||
insert_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
def get_serial_number(cardinal):
|
||||
"""
|
||||
any alphanumerical character sequence with at least one number with length greater equal to 3
|
||||
"""
|
||||
digit = pynini.compose(cardinal.graph_no_exception, DAMO_DIGIT)
|
||||
character = digit | DAMO_ALPHA
|
||||
sequence = character + pynini.closure(pynutil.delete(" ") + character, 2)
|
||||
sequence = sequence @ (pynini.closure(DAMO_ALNUM) + DAMO_DIGIT + pynini.closure(DAMO_ALNUM))
|
||||
return sequence.optimize()
|
||||
|
||||
|
||||
class TelephoneFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying telephone numbers, e.g.
|
||||
one two three one two three five six seven eight -> { number_part: "123-123-5678" }
|
||||
|
||||
This class also support card number and IP format.
|
||||
"one two three dot one double three dot o dot four o" -> { number_part: "123.133.0.40"}
|
||||
|
||||
"three two double seven three two one four three two one four three double zero five" ->
|
||||
{ number_part: 3277 3214 3214 3005}
|
||||
|
||||
Args:
|
||||
cardinal: CardinalFst
|
||||
"""
|
||||
|
||||
def __init__(self, cardinal: GraphFst):
|
||||
super().__init__(name="telephone", kind="classify")
|
||||
# country code, number_part, extension
|
||||
digit_to_str = (
|
||||
pynini.invert(pynini.string_file(get_abs_path("data/numbers/digit.tsv")).optimize())
|
||||
| pynini.cross("0", pynini.union("o", "oh", "zero")).optimize()
|
||||
)
|
||||
|
||||
str_to_digit = pynini.invert(digit_to_str)
|
||||
|
||||
double_digit = pynini.union(
|
||||
*[
|
||||
pynini.cross(
|
||||
pynini.project(str(i) @ digit_to_str, "output")
|
||||
+ pynini.accep(" ")
|
||||
+ pynini.project(str(i) @ digit_to_str, "output"),
|
||||
pynutil.insert("double ") + pynini.project(str(i) @ digit_to_str, "output"),
|
||||
)
|
||||
for i in range(10)
|
||||
]
|
||||
)
|
||||
double_digit.invert()
|
||||
|
||||
# to handle cases like "one twenty three"
|
||||
two_digit_cardinal = pynini.compose(cardinal.graph_no_exception, DAMO_DIGIT**2)
|
||||
double_digit_to_digit = (
|
||||
pynini.compose(double_digit, str_to_digit + pynutil.delete(" ") + str_to_digit)
|
||||
| two_digit_cardinal
|
||||
)
|
||||
|
||||
single_or_double_digit = (
|
||||
pynutil.add_weight(double_digit_to_digit, -0.0001) | str_to_digit
|
||||
).optimize()
|
||||
single_or_double_digit |= (
|
||||
single_or_double_digit
|
||||
+ pynini.closure(
|
||||
pynutil.add_weight(pynutil.delete(" ") + single_or_double_digit, 0.0001)
|
||||
)
|
||||
).optimize()
|
||||
|
||||
number_part = pynini.compose(
|
||||
single_or_double_digit,
|
||||
DAMO_DIGIT**3
|
||||
+ pynutil.insert("-")
|
||||
+ DAMO_DIGIT**3
|
||||
+ pynutil.insert("-")
|
||||
+ DAMO_DIGIT**4,
|
||||
).optimize()
|
||||
number_part = (
|
||||
pynutil.insert('number_part: "') + number_part.optimize() + pynutil.insert('"')
|
||||
)
|
||||
|
||||
cardinal_option = pynini.compose(single_or_double_digit, DAMO_DIGIT ** (2, 3))
|
||||
|
||||
country_code = (
|
||||
pynutil.insert('country_code: "')
|
||||
+ pynini.closure(pynini.cross("plus ", "+"), 0, 1)
|
||||
+ (
|
||||
(pynini.closure(str_to_digit + pynutil.delete(" "), 0, 2) + str_to_digit)
|
||||
| cardinal_option
|
||||
)
|
||||
+ pynutil.insert('"')
|
||||
)
|
||||
|
||||
optional_country_code = pynini.closure(
|
||||
country_code + pynutil.delete(" ") + insert_space, 0, 1
|
||||
).optimize()
|
||||
graph = optional_country_code + number_part
|
||||
|
||||
# credit card number
|
||||
space_four_digits = insert_space + DAMO_DIGIT**4
|
||||
credit_card_graph = pynini.compose(
|
||||
single_or_double_digit, DAMO_DIGIT**4 + space_four_digits**3
|
||||
).optimize()
|
||||
graph |= (
|
||||
pynutil.insert('number_part: "') + credit_card_graph.optimize() + pynutil.insert('"')
|
||||
)
|
||||
|
||||
# SSN
|
||||
ssn_graph = pynini.compose(
|
||||
single_or_double_digit,
|
||||
DAMO_DIGIT**3
|
||||
+ pynutil.insert("-")
|
||||
+ DAMO_DIGIT**2
|
||||
+ pynutil.insert("-")
|
||||
+ DAMO_DIGIT**4,
|
||||
).optimize()
|
||||
graph |= pynutil.insert('number_part: "') + ssn_graph.optimize() + pynutil.insert('"')
|
||||
|
||||
# ip
|
||||
digit_or_double = (
|
||||
pynini.closure(str_to_digit + pynutil.delete(" "), 0, 1) + double_digit_to_digit
|
||||
)
|
||||
digit_or_double |= double_digit_to_digit + pynini.closure(
|
||||
pynutil.delete(" ") + str_to_digit, 0, 1
|
||||
)
|
||||
digit_or_double |= str_to_digit + (pynutil.delete(" ") + str_to_digit) ** (0, 2)
|
||||
digit_or_double |= cardinal_option
|
||||
digit_or_double = digit_or_double.optimize()
|
||||
|
||||
ip_graph = digit_or_double + (pynini.cross(" dot ", ".") + digit_or_double) ** 3
|
||||
|
||||
graph |= pynutil.insert('number_part: "') + ip_graph.optimize() + pynutil.insert('"')
|
||||
graph |= (
|
||||
pynutil.insert('number_part: "')
|
||||
+ pynutil.add_weight(get_serial_number(cardinal=cardinal), weight=0.0001)
|
||||
+ pynutil.insert('"')
|
||||
)
|
||||
|
||||
final_graph = self.add_tokens(graph)
|
||||
self.fst = final_graph.optimize()
|
||||
@@ -0,0 +1,139 @@
|
||||
import pynini
|
||||
from fun_text_processing.inverse_text_normalization.en.taggers.cardinal import CardinalFst
|
||||
from fun_text_processing.inverse_text_normalization.en.utils import get_abs_path, num_to_word
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
GraphFst,
|
||||
convert_space,
|
||||
delete_extra_space,
|
||||
delete_space,
|
||||
insert_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class TimeFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying time
|
||||
e.g. twelve thirty -> time { hours: "12" minutes: "30" }
|
||||
e.g. twelve past one -> time { minutes: "12" hours: "1" }
|
||||
e.g. two o clock a m -> time { hours: "2" suffix: "a.m." }
|
||||
e.g. quarter to two -> time { hours: "1" minutes: "45" }
|
||||
e.g. quarter past two -> time { hours: "2" minutes: "15" }
|
||||
e.g. half past two -> time { hours: "2" minutes: "30" }
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(name="time", kind="classify")
|
||||
# hours, minutes, seconds, suffix, zone, style, speak_period
|
||||
|
||||
suffix_graph = pynini.string_file(get_abs_path("data/time/time_suffix.tsv"))
|
||||
time_zone_graph = pynini.invert(pynini.string_file(get_abs_path("data/time/time_zone.tsv")))
|
||||
to_hour_graph = pynini.string_file(get_abs_path("data/time/to_hour.tsv"))
|
||||
minute_to_graph = pynini.string_file(get_abs_path("data/time/minute_to.tsv"))
|
||||
|
||||
# only used for < 1000 thousand -> 0 weight
|
||||
cardinal = pynutil.add_weight(CardinalFst().graph_no_exception, weight=-0.7)
|
||||
|
||||
labels_hour = [num_to_word(x) for x in range(0, 24)]
|
||||
labels_minute_single = [num_to_word(x) for x in range(1, 10)]
|
||||
labels_minute_double = [num_to_word(x) for x in range(10, 60)]
|
||||
|
||||
graph_hour = pynini.union(*labels_hour) @ cardinal
|
||||
|
||||
graph_minute_single = pynini.union(*labels_minute_single) @ cardinal
|
||||
graph_minute_double = pynini.union(*labels_minute_double) @ cardinal
|
||||
graph_minute_verbose = pynini.cross("half", "30") | pynini.cross("quarter", "15")
|
||||
oclock = pynini.cross(pynini.union("o' clock", "o clock", "o'clock", "oclock"), "")
|
||||
|
||||
final_graph_hour = pynutil.insert('hours: "') + graph_hour + pynutil.insert('"')
|
||||
graph_minute = (
|
||||
oclock + pynutil.insert("00")
|
||||
| pynutil.delete("o") + delete_space + graph_minute_single
|
||||
| graph_minute_double
|
||||
)
|
||||
final_suffix = (
|
||||
pynutil.insert('suffix: "') + convert_space(suffix_graph) + pynutil.insert('"')
|
||||
)
|
||||
final_suffix = delete_space + insert_space + final_suffix
|
||||
final_suffix_optional = pynini.closure(final_suffix, 0, 1)
|
||||
final_time_zone_optional = pynini.closure(
|
||||
delete_space
|
||||
+ insert_space
|
||||
+ pynutil.insert('zone: "')
|
||||
+ convert_space(time_zone_graph)
|
||||
+ pynutil.insert('"'),
|
||||
0,
|
||||
1,
|
||||
)
|
||||
|
||||
# five o' clock
|
||||
# two o eight, two thirty five (am/pm)
|
||||
# two pm/am
|
||||
graph_hm = (
|
||||
final_graph_hour
|
||||
+ delete_extra_space
|
||||
+ pynutil.insert('minutes: "')
|
||||
+ graph_minute
|
||||
+ pynutil.insert('"')
|
||||
)
|
||||
# 10 past four, quarter past four, half past four
|
||||
graph_m_past_h = (
|
||||
pynutil.insert('minutes: "')
|
||||
+ pynini.union(graph_minute_single, graph_minute_double, graph_minute_verbose)
|
||||
+ pynutil.insert('"')
|
||||
+ delete_space
|
||||
+ pynutil.delete("past")
|
||||
+ delete_extra_space
|
||||
+ final_graph_hour
|
||||
)
|
||||
|
||||
graph_quarter_time = (
|
||||
pynutil.insert('minutes: "')
|
||||
+ pynini.cross("quarter", "45")
|
||||
+ pynutil.insert('"')
|
||||
+ delete_space
|
||||
+ pynutil.delete(pynini.union("to", "till"))
|
||||
+ delete_extra_space
|
||||
+ pynutil.insert('hours: "')
|
||||
+ to_hour_graph
|
||||
+ pynutil.insert('"')
|
||||
)
|
||||
|
||||
graph_m_to_h_suffix_time = (
|
||||
pynutil.insert('minutes: "')
|
||||
+ ((graph_minute_single | graph_minute_double).optimize() @ minute_to_graph)
|
||||
+ pynutil.insert('"')
|
||||
+ pynini.closure(
|
||||
delete_space + pynutil.delete(pynini.union("min", "mins", "minute", "minutes")),
|
||||
0,
|
||||
1,
|
||||
)
|
||||
+ delete_space
|
||||
+ pynutil.delete(pynini.union("to", "till"))
|
||||
+ delete_extra_space
|
||||
+ pynutil.insert('hours: "')
|
||||
+ to_hour_graph
|
||||
+ pynutil.insert('"')
|
||||
+ final_suffix
|
||||
)
|
||||
|
||||
graph_h = (
|
||||
final_graph_hour
|
||||
+ delete_extra_space
|
||||
+ pynutil.insert('minutes: "')
|
||||
+ (pynutil.insert("00") | graph_minute)
|
||||
+ pynutil.insert('"')
|
||||
+ final_suffix
|
||||
+ final_time_zone_optional
|
||||
)
|
||||
final_graph = (
|
||||
(graph_hm | graph_m_past_h | graph_quarter_time)
|
||||
+ final_suffix_optional
|
||||
+ final_time_zone_optional
|
||||
)
|
||||
final_graph |= graph_h
|
||||
final_graph |= graph_m_to_h_suffix_time
|
||||
|
||||
final_graph = self.add_tokens(final_graph.optimize())
|
||||
|
||||
self.fst = final_graph.optimize()
|
||||
@@ -0,0 +1,102 @@
|
||||
import os
|
||||
|
||||
import pynini
|
||||
from fun_text_processing.inverse_text_normalization.en.taggers.cardinal import CardinalFst
|
||||
from fun_text_processing.inverse_text_normalization.en.taggers.date import DateFst
|
||||
from fun_text_processing.inverse_text_normalization.en.taggers.decimal import DecimalFst
|
||||
from fun_text_processing.inverse_text_normalization.en.taggers.electronic import ElectronicFst
|
||||
from fun_text_processing.inverse_text_normalization.en.taggers.measure import MeasureFst
|
||||
from fun_text_processing.inverse_text_normalization.en.taggers.money import MoneyFst
|
||||
from fun_text_processing.inverse_text_normalization.en.taggers.ordinal import OrdinalFst
|
||||
from fun_text_processing.inverse_text_normalization.en.taggers.punctuation import PunctuationFst
|
||||
from fun_text_processing.inverse_text_normalization.en.taggers.telephone import TelephoneFst
|
||||
from fun_text_processing.inverse_text_normalization.en.taggers.time import TimeFst
|
||||
from fun_text_processing.inverse_text_normalization.en.taggers.whitelist import WhiteListFst
|
||||
from fun_text_processing.inverse_text_normalization.en.taggers.word import WordFst
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
GraphFst,
|
||||
delete_extra_space,
|
||||
delete_space,
|
||||
generator_main,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
import logging
|
||||
|
||||
|
||||
class ClassifyFst(GraphFst):
|
||||
"""
|
||||
Final class that composes all other classification grammars. This class can process an entire sentence, that is lower cased.
|
||||
For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File.
|
||||
More details to deployment at NeMo/tools/text_processing_deployment.
|
||||
|
||||
Args:
|
||||
cache_dir: path to a dir with .far grammar file. Set to None to avoid using cache.
|
||||
overwrite_cache: set to True to overwrite .far files
|
||||
"""
|
||||
|
||||
def __init__(self, cache_dir: str = None, overwrite_cache: bool = False):
|
||||
super().__init__(name="tokenize_and_classify", kind="classify")
|
||||
|
||||
far_file = None
|
||||
if cache_dir is not None and cache_dir != "None":
|
||||
os.makedirs(cache_dir, exist_ok=True)
|
||||
far_file = os.path.join(cache_dir, "_en_itn.far")
|
||||
if not overwrite_cache and far_file and os.path.exists(far_file):
|
||||
self.fst = pynini.Far(far_file, mode="r")["tokenize_and_classify"]
|
||||
logging.info(f"ClassifyFst.fst was restored from {far_file}.")
|
||||
else:
|
||||
logging.info(f"Creating ClassifyFst grammars.")
|
||||
cardinal = CardinalFst()
|
||||
cardinal_graph = cardinal.fst
|
||||
|
||||
ordinal = OrdinalFst(cardinal)
|
||||
ordinal_graph = ordinal.fst
|
||||
|
||||
decimal = DecimalFst(cardinal)
|
||||
decimal_graph = decimal.fst
|
||||
|
||||
measure_graph = MeasureFst(cardinal=cardinal, decimal=decimal).fst
|
||||
date_graph = DateFst(ordinal=ordinal).fst
|
||||
word_graph = WordFst().fst
|
||||
time_graph = TimeFst().fst
|
||||
money_graph = MoneyFst(cardinal=cardinal, decimal=decimal).fst
|
||||
whitelist_graph = WhiteListFst().fst
|
||||
punct_graph = PunctuationFst().fst
|
||||
electronic_graph = ElectronicFst().fst
|
||||
telephone_graph = TelephoneFst(cardinal).fst
|
||||
|
||||
classify = (
|
||||
pynutil.add_weight(whitelist_graph, 1.01)
|
||||
| pynutil.add_weight(time_graph, 1.1)
|
||||
| pynutil.add_weight(date_graph, 1.09)
|
||||
| pynutil.add_weight(decimal_graph, 1.1)
|
||||
| pynutil.add_weight(measure_graph, 1.1)
|
||||
| pynutil.add_weight(cardinal_graph, 1.1)
|
||||
| pynutil.add_weight(ordinal_graph, 1.1)
|
||||
| pynutil.add_weight(money_graph, 1.1)
|
||||
| pynutil.add_weight(telephone_graph, 1.1)
|
||||
| pynutil.add_weight(electronic_graph, 1.1)
|
||||
| pynutil.add_weight(word_graph, 100)
|
||||
)
|
||||
|
||||
punct = (
|
||||
pynutil.insert("tokens { ")
|
||||
+ pynutil.add_weight(punct_graph, weight=1.1)
|
||||
+ pynutil.insert(" }")
|
||||
)
|
||||
token = pynutil.insert("tokens { ") + classify + pynutil.insert(" }")
|
||||
token_plus_punct = (
|
||||
pynini.closure(punct + pynutil.insert(" "))
|
||||
+ token
|
||||
+ pynini.closure(pynutil.insert(" ") + punct)
|
||||
)
|
||||
|
||||
graph = token_plus_punct + pynini.closure(delete_extra_space + token_plus_punct)
|
||||
graph = delete_space + graph + delete_space
|
||||
|
||||
self.fst = graph.optimize()
|
||||
|
||||
if far_file:
|
||||
generator_main(far_file, {"tokenize_and_classify": self.fst})
|
||||
logging.info(f"ClassifyFst grammars are saved to {far_file}.")
|
||||
@@ -0,0 +1,19 @@
|
||||
import pynini
|
||||
from fun_text_processing.inverse_text_normalization.en.utils import get_abs_path
|
||||
from fun_text_processing.text_normalization.en.graph_utils import GraphFst, convert_space
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class WhiteListFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying whitelisted tokens
|
||||
e.g. misses -> tokens { name: "mrs." }
|
||||
This class has highest priority among all classifier grammars. Whitelisted tokens are defined and loaded from "data/whitelist.tsv".
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(name="whitelist", kind="classify")
|
||||
|
||||
whitelist = pynini.string_file(get_abs_path("data/whitelist.tsv")).invert()
|
||||
graph = pynutil.insert('name: "') + convert_space(whitelist) + pynutil.insert('"')
|
||||
self.fst = graph.optimize()
|
||||
@@ -0,0 +1,15 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import DAMO_NOT_SPACE, GraphFst
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class WordFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for classifying plain tokens, that do not belong to any special class. This can be considered as the default class.
|
||||
e.g. sleep -> tokens { name: "sleep" }
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(name="word", kind="classify")
|
||||
word = pynutil.insert('name: "') + pynini.closure(DAMO_NOT_SPACE, 1) + pynutil.insert('"')
|
||||
self.fst = word.optimize()
|
||||
@@ -0,0 +1,33 @@
|
||||
import os
|
||||
from typing import Union
|
||||
|
||||
import inflect
|
||||
|
||||
_inflect = inflect.engine()
|
||||
|
||||
|
||||
def num_to_word(x: Union[str, int]):
|
||||
"""
|
||||
converts integer to spoken representation
|
||||
|
||||
Args
|
||||
x: integer
|
||||
|
||||
Returns: spoken representation
|
||||
"""
|
||||
if isinstance(x, int):
|
||||
x = str(x)
|
||||
x = _inflect.number_to_words(str(x)).replace("-", " ").replace(",", "")
|
||||
return x
|
||||
|
||||
|
||||
def get_abs_path(rel_path):
|
||||
"""
|
||||
Get absolute path
|
||||
|
||||
Args:
|
||||
rel_path: relative path to this file
|
||||
|
||||
Returns absolute path
|
||||
"""
|
||||
return os.path.dirname(os.path.abspath(__file__)) + "/" + rel_path
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
DAMO_NOT_QUOTE,
|
||||
GraphFst,
|
||||
delete_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class CardinalFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for verbalizing cardinal
|
||||
e.g. cardinal { integer: "23" negative: "-" } -> -23
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(name="cardinal", kind="verbalize")
|
||||
optional_sign = pynini.closure(
|
||||
pynutil.delete("negative:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ DAMO_NOT_QUOTE
|
||||
+ pynutil.delete('"')
|
||||
+ delete_space,
|
||||
0,
|
||||
1,
|
||||
)
|
||||
graph = (
|
||||
pynutil.delete("integer:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.closure(DAMO_NOT_QUOTE, 1)
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
self.numbers = graph
|
||||
graph = optional_sign + graph
|
||||
delete_tokens = self.delete_tokens(graph)
|
||||
self.fst = delete_tokens.optimize()
|
||||
@@ -0,0 +1,70 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
DAMO_NOT_QUOTE,
|
||||
GraphFst,
|
||||
delete_extra_space,
|
||||
delete_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class DateFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for verbalizing date, e.g.
|
||||
date { month: "january" day: "5" year: "2012" preserve_order: true } -> february 5 2012
|
||||
date { day: "5" month: "january" year: "2012" preserve_order: true } -> 5 february 2012
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(name="date", kind="verbalize")
|
||||
month = (
|
||||
pynutil.delete("month:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.closure(DAMO_NOT_QUOTE, 1)
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
day = (
|
||||
pynutil.delete("day:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.closure(DAMO_NOT_QUOTE, 1)
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
year = (
|
||||
pynutil.delete("year:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.closure(DAMO_NOT_QUOTE, 1)
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
|
||||
# month (day) year
|
||||
graph_mdy = (
|
||||
month
|
||||
+ pynini.closure(delete_extra_space + day, 0, 1)
|
||||
+ pynini.closure(delete_extra_space + year, 0, 1)
|
||||
)
|
||||
|
||||
# (day) month year
|
||||
graph_dmy = (
|
||||
pynini.closure(day + delete_extra_space, 0, 1)
|
||||
+ month
|
||||
+ pynini.closure(delete_extra_space + year, 0, 1)
|
||||
)
|
||||
|
||||
optional_preserve_order = pynini.closure(
|
||||
pynutil.delete("preserve_order:") + delete_space + pynutil.delete("true") + delete_space
|
||||
| pynutil.delete("field_order:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ DAMO_NOT_QUOTE
|
||||
+ pynutil.delete('"')
|
||||
+ delete_space
|
||||
)
|
||||
|
||||
final_graph = (graph_mdy | year | graph_dmy) + delete_space + optional_preserve_order
|
||||
|
||||
delete_tokens = self.delete_tokens(final_graph)
|
||||
self.fst = delete_tokens.optimize()
|
||||
@@ -0,0 +1,48 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
DAMO_NOT_QUOTE,
|
||||
GraphFst,
|
||||
delete_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class DecimalFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for verbalizing decimal, e.g.
|
||||
decimal { negative: "true" integer_part: "12" fractional_part: "5006" quantity: "billion" } -> -12.5006 billion
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(name="decimal", kind="verbalize")
|
||||
optionl_sign = pynini.closure(pynini.cross('negative: "true"', "-") + delete_space, 0, 1)
|
||||
integer = (
|
||||
pynutil.delete("integer_part:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.closure(DAMO_NOT_QUOTE, 1)
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
optional_integer = pynini.closure(integer + delete_space, 0, 1)
|
||||
fractional = (
|
||||
pynutil.insert(".")
|
||||
+ pynutil.delete("fractional_part:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.closure(DAMO_NOT_QUOTE, 1)
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
optional_fractional = pynini.closure(fractional + delete_space, 0, 1)
|
||||
quantity = (
|
||||
pynutil.delete("quantity:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.closure(DAMO_NOT_QUOTE, 1)
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
optional_quantity = pynini.closure(pynutil.insert(" ") + quantity + delete_space, 0, 1)
|
||||
graph = optional_integer + optional_fractional + optional_quantity
|
||||
self.numbers = graph
|
||||
graph = optionl_sign + graph
|
||||
delete_tokens = self.delete_tokens(graph)
|
||||
self.fst = delete_tokens.optimize()
|
||||
@@ -0,0 +1,45 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
DAMO_NOT_QUOTE,
|
||||
GraphFst,
|
||||
delete_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class ElectronicFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for verbalizing electronic
|
||||
e.g. tokens { electronic { username: "cdf1" domain: "abc.edu" } } -> cdf1@abc.edu
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(name="electronic", kind="verbalize")
|
||||
user_name = (
|
||||
pynutil.delete("username:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.closure(DAMO_NOT_QUOTE, 1)
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
domain = (
|
||||
pynutil.delete("domain:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.closure(DAMO_NOT_QUOTE, 1)
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
|
||||
protocol = (
|
||||
pynutil.delete("protocol:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.closure(DAMO_NOT_QUOTE, 1)
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
|
||||
graph = user_name + delete_space + pynutil.insert("@") + domain
|
||||
graph |= protocol
|
||||
|
||||
delete_tokens = self.delete_tokens(graph)
|
||||
self.fst = delete_tokens.optimize()
|
||||
@@ -0,0 +1,10 @@
|
||||
from fun_text_processing.text_normalization.en.graph_utils import GraphFst
|
||||
|
||||
|
||||
class FractionFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for verbalizing fraction,
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(name="fraction", kind="verbalize")
|
||||
@@ -0,0 +1,47 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import DAMO_CHAR, GraphFst, delete_space
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class MeasureFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for verbalizing measure, e.g.
|
||||
measure { negative: "true" cardinal { integer: "12" } units: "kg" } -> -12 kg
|
||||
|
||||
Args:
|
||||
decimal: DecimalFst
|
||||
cardinal: CardinalFst
|
||||
"""
|
||||
|
||||
def __init__(self, decimal: GraphFst, cardinal: GraphFst):
|
||||
super().__init__(name="measure", kind="verbalize")
|
||||
optional_sign = pynini.closure(pynini.cross('negative: "true"', "-"), 0, 1)
|
||||
unit = (
|
||||
pynutil.delete("units:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.closure(DAMO_CHAR - " ", 1)
|
||||
+ pynutil.delete('"')
|
||||
+ delete_space
|
||||
)
|
||||
graph_decimal = (
|
||||
pynutil.delete("decimal {")
|
||||
+ delete_space
|
||||
+ optional_sign
|
||||
+ delete_space
|
||||
+ decimal.numbers
|
||||
+ delete_space
|
||||
+ pynutil.delete("}")
|
||||
)
|
||||
graph_cardinal = (
|
||||
pynutil.delete("cardinal {")
|
||||
+ delete_space
|
||||
+ optional_sign
|
||||
+ delete_space
|
||||
+ cardinal.numbers
|
||||
+ delete_space
|
||||
+ pynutil.delete("}")
|
||||
)
|
||||
graph = (graph_cardinal | graph_decimal) + delete_space + pynutil.insert(" ") + unit
|
||||
delete_tokens = self.delete_tokens(graph)
|
||||
self.fst = delete_tokens.optimize()
|
||||
@@ -0,0 +1,26 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import DAMO_CHAR, GraphFst, delete_space
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class MoneyFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for verbalizing money, e.g.
|
||||
money { integer_part: "12" fractional_part: "05" currency: "$" } -> $12.05
|
||||
|
||||
Args:
|
||||
decimal: DecimalFst
|
||||
"""
|
||||
|
||||
def __init__(self, decimal: GraphFst):
|
||||
super().__init__(name="money", kind="verbalize")
|
||||
unit = (
|
||||
pynutil.delete("currency:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.closure(DAMO_CHAR - " ", 1)
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
graph = unit + delete_space + decimal.numbers
|
||||
delete_tokens = self.delete_tokens(graph)
|
||||
self.fst = delete_tokens.optimize()
|
||||
@@ -0,0 +1,48 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
DAMO_NOT_QUOTE,
|
||||
DAMO_SIGMA,
|
||||
GraphFst,
|
||||
delete_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class OrdinalFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for verbalizing ordinal, e.g.
|
||||
ordinal { integer: "13" } -> 13th
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(name="ordinal", kind="verbalize")
|
||||
graph = (
|
||||
pynutil.delete("integer:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.closure(DAMO_NOT_QUOTE, 1)
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
convert_eleven = pynini.cross("11", "11th")
|
||||
convert_twelve = pynini.cross("12", "12th")
|
||||
convert_thirteen = pynini.cross("13", "13th")
|
||||
convert_one = pynini.cross("1", "1st")
|
||||
convert_two = pynini.cross("2", "2nd")
|
||||
convert_three = pynini.cross("3", "3rd")
|
||||
convert_rest = pynutil.insert("th", weight=0.01)
|
||||
|
||||
suffix = pynini.cdrewrite(
|
||||
convert_eleven
|
||||
| convert_twelve
|
||||
| convert_thirteen
|
||||
| convert_one
|
||||
| convert_two
|
||||
| convert_three
|
||||
| convert_rest,
|
||||
"",
|
||||
"[EOS]",
|
||||
DAMO_SIGMA,
|
||||
)
|
||||
graph = graph @ suffix
|
||||
delete_tokens = self.delete_tokens(graph)
|
||||
self.fst = delete_tokens.optimize()
|
||||
@@ -0,0 +1,30 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import DAMO_NOT_QUOTE, GraphFst
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class TelephoneFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for verbalizing telephone, e.g.
|
||||
telephone { number_part: "123-123-5678" }
|
||||
-> 123-123-5678
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(name="telephone", kind="verbalize")
|
||||
|
||||
number_part = (
|
||||
pynutil.delete('number_part: "')
|
||||
+ pynini.closure(DAMO_NOT_QUOTE, 1)
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
optional_country_code = pynini.closure(
|
||||
pynutil.delete('country_code: "')
|
||||
+ pynini.closure(DAMO_NOT_QUOTE, 1)
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.accep(" "),
|
||||
0,
|
||||
1,
|
||||
)
|
||||
delete_tokens = self.delete_tokens(optional_country_code + number_part)
|
||||
self.fst = delete_tokens.optimize()
|
||||
@@ -0,0 +1,68 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
DAMO_CHAR,
|
||||
DAMO_DIGIT,
|
||||
GraphFst,
|
||||
delete_space,
|
||||
insert_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class TimeFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for verbalizing time, e.g.
|
||||
time { hours: "12" minutes: "30" } -> 12:30
|
||||
time { hours: "1" minutes: "12" } -> 01:12
|
||||
time { hours: "2" suffix: "a.m." } -> 02:00 a.m.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(name="time", kind="verbalize")
|
||||
add_leading_zero_to_double_digit = (DAMO_DIGIT + DAMO_DIGIT) | (
|
||||
pynutil.insert("0") + DAMO_DIGIT
|
||||
)
|
||||
hour = (
|
||||
pynutil.delete("hours:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.closure(DAMO_DIGIT, 1)
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
minute = (
|
||||
pynutil.delete("minutes:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.closure(DAMO_DIGIT, 1)
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
suffix = (
|
||||
delete_space
|
||||
+ insert_space
|
||||
+ pynutil.delete("suffix:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.closure(DAMO_CHAR - " ", 1)
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
optional_suffix = pynini.closure(suffix, 0, 1)
|
||||
zone = (
|
||||
delete_space
|
||||
+ insert_space
|
||||
+ pynutil.delete("zone:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.closure(DAMO_CHAR - " ", 1)
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
optional_zone = pynini.closure(zone, 0, 1)
|
||||
graph = (
|
||||
hour @ add_leading_zero_to_double_digit
|
||||
+ delete_space
|
||||
+ pynutil.insert(":")
|
||||
+ (minute @ add_leading_zero_to_double_digit)
|
||||
+ optional_suffix
|
||||
+ optional_zone
|
||||
)
|
||||
delete_tokens = self.delete_tokens(graph)
|
||||
self.fst = delete_tokens.optimize()
|
||||
@@ -0,0 +1,47 @@
|
||||
from fun_text_processing.inverse_text_normalization.en.verbalizers.cardinal import CardinalFst
|
||||
from fun_text_processing.inverse_text_normalization.en.verbalizers.date import DateFst
|
||||
from fun_text_processing.inverse_text_normalization.en.verbalizers.decimal import DecimalFst
|
||||
from fun_text_processing.inverse_text_normalization.en.verbalizers.electronic import ElectronicFst
|
||||
from fun_text_processing.inverse_text_normalization.en.verbalizers.measure import MeasureFst
|
||||
from fun_text_processing.inverse_text_normalization.en.verbalizers.money import MoneyFst
|
||||
from fun_text_processing.inverse_text_normalization.en.verbalizers.ordinal import OrdinalFst
|
||||
from fun_text_processing.inverse_text_normalization.en.verbalizers.telephone import TelephoneFst
|
||||
from fun_text_processing.inverse_text_normalization.en.verbalizers.time import TimeFst
|
||||
from fun_text_processing.inverse_text_normalization.en.verbalizers.whitelist import WhiteListFst
|
||||
from fun_text_processing.text_normalization.en.graph_utils import GraphFst
|
||||
|
||||
|
||||
class VerbalizeFst(GraphFst):
|
||||
"""
|
||||
Composes other verbalizer grammars.
|
||||
For deployment, this grammar will be compiled and exported to OpenFst Finate State Archiv (FAR) File.
|
||||
More details to deployment at NeMo/tools/text_processing_deployment.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(name="verbalize", kind="verbalize")
|
||||
cardinal = CardinalFst()
|
||||
cardinal_graph = cardinal.fst
|
||||
ordinal_graph = OrdinalFst().fst
|
||||
decimal = DecimalFst()
|
||||
decimal_graph = decimal.fst
|
||||
measure_graph = MeasureFst(decimal=decimal, cardinal=cardinal).fst
|
||||
money_graph = MoneyFst(decimal=decimal).fst
|
||||
time_graph = TimeFst().fst
|
||||
date_graph = DateFst().fst
|
||||
whitelist_graph = WhiteListFst().fst
|
||||
telephone_graph = TelephoneFst().fst
|
||||
electronic_graph = ElectronicFst().fst
|
||||
graph = (
|
||||
time_graph
|
||||
| date_graph
|
||||
| money_graph
|
||||
| measure_graph
|
||||
| ordinal_graph
|
||||
| decimal_graph
|
||||
| cardinal_graph
|
||||
| whitelist_graph
|
||||
| telephone_graph
|
||||
| electronic_graph
|
||||
)
|
||||
self.fst = graph
|
||||
@@ -0,0 +1,33 @@
|
||||
import pynini
|
||||
from fun_text_processing.inverse_text_normalization.en.verbalizers.verbalize import VerbalizeFst
|
||||
from fun_text_processing.inverse_text_normalization.en.verbalizers.word import WordFst
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
GraphFst,
|
||||
delete_extra_space,
|
||||
delete_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class VerbalizeFinalFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer that verbalizes an entire sentence, e.g.
|
||||
tokens { name: "its" } tokens { time { hours: "12" minutes: "30" } } tokens { name: "now" } -> its 12:30 now
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(name="verbalize_final", kind="verbalize")
|
||||
verbalize = VerbalizeFst().fst
|
||||
word = WordFst().fst
|
||||
types = verbalize | word
|
||||
graph = (
|
||||
pynutil.delete("tokens")
|
||||
+ delete_space
|
||||
+ pynutil.delete("{")
|
||||
+ delete_space
|
||||
+ types
|
||||
+ delete_space
|
||||
+ pynutil.delete("}")
|
||||
)
|
||||
graph = delete_space + pynini.closure(graph + delete_extra_space) + graph + delete_space
|
||||
self.fst = graph
|
||||
@@ -0,0 +1,27 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
DAMO_CHAR,
|
||||
DAMO_SIGMA,
|
||||
GraphFst,
|
||||
delete_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class WhiteListFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for verbalizing whitelist
|
||||
e.g. tokens { name: "mrs." } -> mrs.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(name="whitelist", kind="verbalize")
|
||||
graph = (
|
||||
pynutil.delete("name:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ pynini.closure(DAMO_CHAR - " ", 1)
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
graph = graph @ pynini.cdrewrite(pynini.cross("\u00A0", " "), "", "", DAMO_SIGMA)
|
||||
self.fst = graph.optimize()
|
||||
@@ -0,0 +1,29 @@
|
||||
import pynini
|
||||
from fun_text_processing.text_normalization.en.graph_utils import (
|
||||
DAMO_CHAR,
|
||||
DAMO_SIGMA,
|
||||
GraphFst,
|
||||
delete_space,
|
||||
)
|
||||
from pynini.lib import pynutil
|
||||
|
||||
|
||||
class WordFst(GraphFst):
|
||||
"""
|
||||
Finite state transducer for verbalizing plain tokens
|
||||
e.g. tokens { name: "sleep" } -> sleep
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(name="word", kind="verbalize")
|
||||
chars = pynini.closure(DAMO_CHAR - " ", 1)
|
||||
char = (
|
||||
pynutil.delete("name:")
|
||||
+ delete_space
|
||||
+ pynutil.delete('"')
|
||||
+ chars
|
||||
+ pynutil.delete('"')
|
||||
)
|
||||
graph = char @ pynini.cdrewrite(pynini.cross("\u00A0", " "), "", "", DAMO_SIGMA)
|
||||
|
||||
self.fst = graph.optimize()
|
||||
@@ -0,0 +1,7 @@
|
||||
from fun_text_processing.inverse_text_normalization.es.taggers.tokenize_and_classify import (
|
||||
ClassifyFst,
|
||||
)
|
||||
from fun_text_processing.inverse_text_normalization.es.verbalizers.verbalize import VerbalizeFst
|
||||
from fun_text_processing.inverse_text_normalization.es.verbalizers.verbalize_final import (
|
||||
VerbalizeFinalFst,
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
€ euros
|
||||
US$ dólares estadounidenses
|
||||
US$ dólares americanos
|
||||
$ dólares
|
||||
$ pesos
|
||||
¥ yenes
|
||||
|
@@ -0,0 +1,6 @@
|
||||
€ euro
|
||||
US$ dólar estadounidense
|
||||
US$ dólar americano
|
||||
$ dólar
|
||||
$ peso
|
||||
¥ yen
|
||||
|
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
com
|
||||
es
|
||||
uk
|
||||
fr
|
||||
net
|
||||
br
|
||||
in
|
||||
ru
|
||||
de
|
||||
it
|
||||
edu
|
||||
co
|
||||
ar
|
||||
bo
|
||||
cl
|
||||
co
|
||||
ec
|
||||
fk
|
||||
gf
|
||||
fy
|
||||
pe
|
||||
py
|
||||
sr
|
||||
ve
|
||||
uy
|
||||
|
@@ -0,0 +1,17 @@
|
||||
gmail g mail
|
||||
gmail
|
||||
nvidia n vidia
|
||||
nvidia
|
||||
outlook
|
||||
hotmail
|
||||
yahoo
|
||||
aol
|
||||
gmx
|
||||
msn
|
||||
live
|
||||
yandex
|
||||
orange
|
||||
wanadoo
|
||||
web
|
||||
comcast
|
||||
bbc
|
||||
|
@@ -0,0 +1,4 @@
|
||||
. punto
|
||||
- guion
|
||||
_ guion bajo
|
||||
/ barra
|
||||
|
@@ -0,0 +1,19 @@
|
||||
cm centímetros
|
||||
g gramos
|
||||
h horas
|
||||
kg kilos
|
||||
kg kilogramos
|
||||
km kilómetros
|
||||
km² kilómetros cuadrados
|
||||
l litros
|
||||
m metros
|
||||
m² metros cuadrados
|
||||
m³ metros cubicos
|
||||
mph millas por hora
|
||||
ml mililitros
|
||||
mm milímetros
|
||||
ms milisegundos
|
||||
min minutos
|
||||
% por ciento
|
||||
% porciento
|
||||
s segundos
|
||||
|
@@ -0,0 +1,19 @@
|
||||
cm centímetro
|
||||
g gramo
|
||||
h hora
|
||||
kg kilo
|
||||
kg kilogramo
|
||||
km kilómetro
|
||||
km² kilómetro cuadrado
|
||||
l litro
|
||||
m metro
|
||||
m² metro cuadrado
|
||||
m³ metro cubico
|
||||
mph milla por hora
|
||||
ml mililitro
|
||||
mm milímetro
|
||||
ms milisegundo
|
||||
min minuto
|
||||
% por ciento
|
||||
% porciento
|
||||
s segundo
|
||||
|
@@ -0,0 +1,12 @@
|
||||
enero
|
||||
febrero
|
||||
marzo
|
||||
abril
|
||||
mayo
|
||||
junio
|
||||
julio
|
||||
agosto
|
||||
septiembre
|
||||
octubre
|
||||
noviembre
|
||||
diciembre
|
||||
|
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
uno 1
|
||||
un 1
|
||||
ún 1
|
||||
una 1
|
||||
dos 2
|
||||
tres 3
|
||||
cuatro 4
|
||||
cinco 5
|
||||
seis 6
|
||||
siete 7
|
||||
ocho 8
|
||||
nueve 9
|
||||
|
@@ -0,0 +1,18 @@
|
||||
ciento 1
|
||||
cien 1
|
||||
doscientos 2
|
||||
doscientas 2
|
||||
trescientos 3
|
||||
trescientas 3
|
||||
cuatrocientos 4
|
||||
cuatrocientas 4
|
||||
quinientos 5
|
||||
quinientas 5
|
||||
seiscientos 6
|
||||
seiscientas 6
|
||||
setecientos 7
|
||||
setecientas 7
|
||||
ochocientos 8
|
||||
ochocientas 8
|
||||
novecientos 9
|
||||
novecientas 9
|
||||
|
@@ -0,0 +1,10 @@
|
||||
diez 10
|
||||
once 11
|
||||
doce 12
|
||||
trece 13
|
||||
catorce 14
|
||||
quince 15
|
||||
dieciséis 16
|
||||
diecisiete 17
|
||||
dieciocho 18
|
||||
diecinueve 19
|
||||
|
@@ -0,0 +1,9 @@
|
||||
veinte 2
|
||||
treinta 3
|
||||
cuarenta 4
|
||||
cincuenta 5
|
||||
cinquenta 5
|
||||
sesenta 6
|
||||
setenta 7
|
||||
ochenta 8
|
||||
noventa 9
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user