Skip to content

Commit acc2a79

Browse files
committed
feat: add Serial rule for English TN (#259)
Add Serial rule to handle mixed alphanumeric sequences like model numbers, product codes, and compound words with numbers: B2A23C => B two A twenty three C covid-19 => covid-nineteen MIG-25 => MIG-twenty five f++ => f plus plus 7-eleven => seven-eleven
1 parent 40ab662 commit acc2a79

2 files changed

Lines changed: 110 additions & 0 deletions

File tree

tn/english/normalizer.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from tn.english.rules.ordinal import Ordinal
2727
from tn.english.rules.punctuation import Punctuation
2828
from tn.english.rules.range import Range
29+
from tn.english.rules.serial import Serial
2930
from tn.english.rules.telephone import Telephone
3031
from tn.english.rules.time import Time
3132
from tn.english.rules.whitelist import WhiteList
@@ -53,6 +54,7 @@ def build_tagger_and_verbalizer(self):
5354
money = Money(cardinal=cardinal, decimal=decimal)
5455
telephone = Telephone()
5556
electronic = Electronic(cardinal=cardinal)
57+
serial = Serial(cardinal=cardinal, ordinal=ordinal)
5658
word = Word(punctuation=punctuation)
5759
whitelist = WhiteList()
5860
rang = Range(date=date, time=time)
@@ -69,6 +71,7 @@ def build_tagger_and_verbalizer(self):
6971
| add_weight(money.tagger, 1.00)
7072
| add_weight(telephone.tagger, 1.00)
7173
| add_weight(electronic.tagger, 1.00)
74+
| add_weight(serial.tagger, 1.01)
7275
| add_weight(whitelist.tagger, 1.00)
7376
| add_weight(rang.tagger, 1.01)
7477
| add_weight(punctuation.tagger, 2.00)
@@ -87,6 +90,7 @@ def build_tagger_and_verbalizer(self):
8790
| money.verbalizer
8891
| telephone.verbalizer
8992
| electronic.verbalizer
93+
| serial.verbalizer
9094
| whitelist.verbalizer
9195
| punctuation.verbalizer
9296
| rang.verbalizer

tn/english/rules/serial.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Copyright (c) 2024 Zhendong Peng (pzd17@tsinghua.org.cn)
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import pynini
16+
from pynini.lib import pynutil
17+
18+
from tn.english.rules.cardinal import Cardinal
19+
from tn.english.rules.ordinal import Ordinal
20+
from tn.processor import Processor
21+
from tn.utils import get_abs_path, load_labels
22+
23+
24+
class Serial(Processor):
25+
26+
def __init__(self, deterministic: bool = False, cardinal=None, ordinal=None):
27+
super().__init__("serial", ordertype="en_tn")
28+
self.deterministic = deterministic
29+
self.cardinal = cardinal or Cardinal(deterministic)
30+
self.ordinal = ordinal or Ordinal(deterministic, cardinal=self.cardinal)
31+
self.build_tagger()
32+
self.build_verbalizer()
33+
34+
def build_tagger(self):
35+
cardinal = self.cardinal
36+
37+
num_graph = pynini.compose(self.DIGIT ** (6, ...), cardinal.single_digits_graph).optimize()
38+
num_graph |= pynini.compose(self.DIGIT ** (1, 5), cardinal.graph).optimize()
39+
num_graph |= pynini.compose(
40+
pynini.accep("0") + pynini.closure(self.DIGIT), cardinal.single_digits_graph
41+
).optimize()
42+
43+
symbols_graph = pynini.string_file(get_abs_path("english/data/whitelist/symbol.tsv")).optimize()
44+
symbols_graph |= pynini.cross("#", "hash")
45+
num_graph |= symbols_graph
46+
47+
symbols = [x[0] for x in load_labels(get_abs_path("english/data/whitelist/symbol.tsv"))]
48+
symbols = pynini.union(*symbols)
49+
digit_symbol = self.DIGIT | symbols
50+
51+
graph_with_space = pynini.compose(
52+
pynini.cdrewrite(pynutil.insert(" "), self.ALPHA | symbols, digit_symbol, self.VSIGMA),
53+
pynini.cdrewrite(pynutil.insert(" "), digit_symbol, self.ALPHA | symbols, self.VSIGMA),
54+
)
55+
56+
delimiter = pynini.accep("-") | pynini.accep("/") | pynini.accep(" ")
57+
58+
alphas = pynini.closure(self.ALPHA, 1)
59+
letter_num = alphas + delimiter + num_graph
60+
num_letter = pynini.closure(num_graph + delimiter, 1) + alphas
61+
next_alpha_or_num = pynini.closure(delimiter + (alphas | num_graph))
62+
63+
serial_graph = letter_num + next_alpha_or_num
64+
serial_graph |= num_letter + next_alpha_or_num
65+
serial_graph |= (
66+
num_graph + delimiter + num_graph + delimiter + num_graph + pynini.closure(delimiter + num_graph)
67+
)
68+
69+
serial_graph = pynini.compose(
70+
pynini.difference(self.VSIGMA, pynini.project(self.ordinal.graph, "input")), serial_graph
71+
).optimize()
72+
73+
serial_graph |= (
74+
pynini.closure(self.NOT_SPACE, 1)
75+
+ (pynini.cross("^2", " squared") | pynini.cross("^3", " cubed")).optimize()
76+
)
77+
78+
serial_graph = (
79+
pynini.closure((serial_graph | num_graph | alphas) + delimiter)
80+
+ serial_graph
81+
+ pynini.closure(delimiter + (serial_graph | num_graph | alphas))
82+
)
83+
84+
serial_graph |= pynini.compose(graph_with_space, serial_graph.optimize()).optimize()
85+
serial_graph = pynini.compose(pynini.closure(self.NOT_SPACE, 2), serial_graph).optimize()
86+
87+
serial_graph = pynini.compose(
88+
pynini.difference(
89+
self.VSIGMA, pynini.closure(self.ALPHA, 1) + pynini.accep("/") + pynini.closure(self.ALPHA, 1)
90+
),
91+
serial_graph,
92+
)
93+
94+
self.graph = serial_graph.optimize()
95+
graph = pynutil.insert('name: "') + self.graph + pynutil.insert('"')
96+
self.tagger = self.add_tokens(graph)
97+
98+
def build_verbalizer(self):
99+
graph = (
100+
pynutil.delete("name:")
101+
+ self.DELETE_SPACE
102+
+ pynutil.delete('"')
103+
+ self.NOT_QUOTE.plus
104+
+ pynutil.delete('"')
105+
)
106+
self.verbalizer = self.delete_tokens(graph)

0 commit comments

Comments
 (0)