Skip to content

Commit 55d5347

Browse files
authored
Update: ruaccent 1.5.5.2
1 parent 9e01ad7 commit 55d5347

15 files changed

+987
-77
lines changed

LICENSE

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Copyright 2023 Denis Petrov
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.

README.md

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# RUAccent
22

3-
RUAccent - это библиотека для автоматической расстановки ударений на русском языке.
3+
RUAccent - это библиотека для автоматической расстановки ударений на русском языке.
4+
5+
**Внимание!!! Смена лицензии на Apache 2.0**
46

57
## Установка
68
С помощью pip
@@ -13,16 +15,14 @@ RUAccent - это библиотека для автоматической ра
1315
```
1416
## Параметры работы
1517

16-
load(omograph_model_size='big_poetry', use_dictionary=True, custom_dict={}, custom_homographs={})
17-
18+
load(omograph_model_size='big_poetry', use_dictionary=True, custom_dict={})
1819

1920
- На данный момент доступно 6 моделей. **big** (рекомендуется к использованию), **medium** и **small**. Рекомендуются к использованию модели версии **poetry**. Их названия **big_poetry**, **medium_poetry**, **small_poetry**.
20-
- Модель **big** имеет 178 миллионов параметров, **medium** 85 миллионов, а **small** 42 миллиона
21+
- Модель **big** имеет 178 миллионов параметров, **medium** 85 миллионов, а **small** 12 миллионов
2122
- Переменная **use_dictionary** отвечает за загрузку всего словаря (требуется больше ОЗУ), иначе все ударения расставляет нейросеть.
22-
- Переменная **custom_homographs** отвечает за добавление своих омографов. Формат такой: `{'слово-омограф': ['вариант ударения 1', 'вариант ударения 2']}`.
2323
- Функция **custom_dict** отвечает за добавление своих вариантов ударений в словарь. Формат такой: `{'слово': 'сл+ово с удар+ением'}`
2424

25-
25+
**Для работы требуется 5 гигабайт ОЗУ**
2626
## Пример использования
2727
```python
2828
from ruaccent import RUAccent
@@ -37,10 +37,4 @@ text = 'ежик нашел в лесу ягоды.'
3737
print(accentizer.process_yo(text))
3838
```
3939

40-
## Датасеты
41-
42-
- [Датасет](https://huggingface.co/datasets/TeraTTS/nkrja_raw) собранный с [НКРЯ](https://ruscorpora.ru/) (удален по просьбе разработчиков НКРЯ)
43-
- [Датасет](https://huggingface.co/datasets/TeraTTS/stress_dataset_sft_proza) использовавшийся для обучения моделей акцентуатора (версия только с прозой)
44-
- [Датасет](https://huggingface.co/datasets/TeraTTS/stress_dataset_sft_poetry) использовавшийся для обучения моделей акцентуатора (версия проза + поэзия)
45-
46-
Файлы моделей и словарей располагаются по [ссылке](https://huggingface.co/TeraTTS/accentuator). Мы будем признательны фидбеку на [telegram аккаунт](https://t.me/chckdskeasfsd)
40+
Файлы моделей и словарей располагаются по [ссылке](https://huggingface.co/ruaccent/accentuator). Мы будем признательны фидбеку на [telegram аккаунт](https://t.me/chckdskeasfsd)

ruaccent/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Russian accentizer"""
22

3-
__version__ = "1.5.4.1"
3+
__version__ = "1.5.5.2"
44

55

6-
from .ruaccent import RUAccent
6+
from .ruaccent import RUAccent

ruaccent/accent_model.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,26 @@
33
from onnxruntime import InferenceSession
44
from .char_tokenizer import CharTokenizer
55

6+
def softmax(x):
7+
e_x = np.exp(x - np.max(x))
8+
return e_x / e_x.sum(axis=-1, keepdims=True)
9+
610
class AccentModel:
711
def __init__(self) -> None:
812
pass
913

10-
def load(self, path):
11-
self.session = InferenceSession(f"{path}/model.onnx", providers=["CPUExecutionProvider"])
14+
def load(self, path, device="CPU"):
15+
self.session = InferenceSession(f"{path}/model.onnx", providers=["CUDAExecutionProvider" if device == "CUDA" else "CPUExecutionProvider"])
1216

1317
with open(f"{path}/config.json", "r") as f:
1418
self.id2label = json.load(f)["id2label"]
1519
self.tokenizer = CharTokenizer.from_pretrained(path)
16-
self.tokenizer.model_input_names = ["input_ids", "attention_mask"]
1720

1821
def render_stress(self, text, pred):
1922
text = list(text)
2023
i = 0
2124
for chunk in pred:
22-
if chunk != "NO":
25+
if chunk['label'] != "NO" and chunk['label'] != "STRESS_SECONDARY" and chunk["score"] >= 0.55:
2326
text[i - 1] = "+" + text[i - 1]
2427
i += 1
2528
text = "".join(text)
@@ -31,7 +34,12 @@ def put_accent(self, word):
3134
outputs = self.session.run(None, inputs)
3235
output_names = {output_key.name: idx for idx, output_key in enumerate(self.session.get_outputs())}
3336
logits = outputs[output_names["logits"]]
37+
probabilities = softmax(logits)
38+
scores = np.max(probabilities, axis=-1)[0]
3439
labels = np.argmax(logits, axis=-1)[0]
35-
labels = [self.id2label[str(label)] for label in labels]
36-
stressed_word = self.render_stress(word, labels)
40+
pred_with_scores = [{'label': self.id2label[str(label)], 'score': float(score)}
41+
for label, score in zip(labels, scores)]
42+
43+
stressed_word = self.render_stress(word, pred_with_scores)
44+
3745
return stressed_word

ruaccent/omograph_model.py

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,67 @@ class OmographModel:
77
def __init__(self):
88
pass
99

10-
def load(self, path):
10+
def load(self, path, device="CPU"):
11+
self.session = InferenceSession(f"{path}/model.onnx", providers=["CUDAExecutionProvider" if device == "CUDA" else "CPUExecutionProvider"])
1112
self.tokenizer = AutoTokenizer.from_pretrained(path)
12-
self.session = InferenceSession(f"{path}/model.onnx", providers=['CPUExecutionProvider'])
13-
13+
1414
def softmax(self, x):
1515
e_x = np.exp(x - np.max(x))
1616
return e_x / e_x.sum()
1717

18-
def classify(self, text, hypotheses):
18+
def group_words(self, words):
19+
groups = {}
20+
for word in words:
21+
parts = word.replace('+', '')
22+
key = parts
23+
groups.setdefault(key, []).append(word)
24+
return list(groups.values())
25+
26+
def transfer_grouping(self, grouped_list, target_list):
27+
new_grouped_list = []
28+
start_index = 0
29+
for group in grouped_list:
30+
group_length = len(group)
31+
new_group = target_list[start_index:start_index + group_length]
32+
new_grouped_list.append(new_group)
33+
start_index += group_length
34+
return new_grouped_list
35+
36+
def classify(self, texts, hypotheses):
1937
hypotheses_probs = []
20-
text = re.sub(r'\s+(?=(?:[,.?!:;…]))', r'', text)
21-
for h in hypotheses:
22-
inputs = self.tokenizer(text, h, return_tensors="np")
38+
preprocessed_texts = [re.sub(r'\s+(?=(?:[,.?!:;…]))', r'', text) for text in texts]
39+
if len(hypotheses) % 2 != 0:
40+
outs = []
41+
grouped_h = self.group_words(hypotheses)
42+
grouped_t = self.transfer_grouping(grouped_h, preprocessed_texts)
43+
for h, t in zip(grouped_h, grouped_t):
44+
probs = []
45+
for hp in h:
46+
inputs = self.tokenizer(t[0], hp, max_length=512, truncation=True, return_tensors="np")
47+
inputs = {k: v.astype(np.int64) for k, v in inputs.items()}
48+
outputs = self.session.run(None, inputs)[0]
49+
outputs = self.softmax(outputs)
50+
prob_label_is_true = [float(p[1]) for p in outputs][0]
51+
probs.append(prob_label_is_true)
52+
#print(h, prob_label_is_true)
53+
outs.append(h[probs.index(max(probs))])
54+
return outs
55+
else:
56+
inputs = self.tokenizer(preprocessed_texts, hypotheses, return_tensors="np", padding=True, truncation=True, max_length=512)
2357
inputs = {k: v.astype(np.int64) for k, v in inputs.items()}
24-
58+
2559
outputs = self.session.run(None, inputs)[0]
2660
outputs = self.softmax(outputs)
27-
prob_label_is_true = [float(p[1]) for p in outputs][0]
28-
hypotheses_probs.append(prob_label_is_true)
29-
return hypotheses[hypotheses_probs.index(max(hypotheses_probs))]
61+
#print(hypotheses)
62+
preprocessed_texts = [(preprocessed_texts[i], preprocessed_texts[i+1]) for i in range(0, len(preprocessed_texts), 2)]
63+
hypotheses = [(hypotheses[i], hypotheses[i+1]) for i in range(0, len(hypotheses), 2)]
64+
65+
for i in range(len(texts)):
66+
prob_label_is_true = float(outputs[i][1])
67+
hypotheses_probs.append(prob_label_is_true)
68+
69+
hypotheses_probs = [(hypotheses_probs[i], hypotheses_probs[i+1]) for i in range(0, len(hypotheses_probs), 2)]
70+
outs = []
71+
for pair1, pair2 in zip(hypotheses, hypotheses_probs):
72+
outs.append(pair1[pair2.index(max(pair2))])
73+
return outs

0 commit comments

Comments
 (0)