Перейти к основному содержимому

Транслитерация в текстовом канале

В этой статье мы настроим бота, чтобы он мог понимать транслитерацию в казахском языке. Бот будет работать в текстовом канале.

В этом языке есть буквы, которых нет на стандартной клавиатуре с английской и русской раскладкой. А мы знаем, что часть пользователей предпочитает именно такие клавиатуры. Например, им приходится вместо иә писать еэ или йэ. Такой бот сможет правильно считывать казахские слова, написанные другой раскладкой.

Решение​

Для наибольшей эффективности применим следующую последовательность действий:

  1. Установить тот системный язык, который нужно распознавать. В нашем случае казахский.
  2. Выбрать классификатора Transformer.
  3. Добавить обучающие фразы, которые содержат транслитерацию.
  4. Создать регулярные выражения.

1. Установка системного языка​

  1. Перейдите на главную страницу JAICP и найдите нужный проект в секции Мои проекты.
  2. Нажмите в карточке проекта и выберите Настройки проекта.
  3. В поле Язык NLU выберите казахский.

2. Использование классификатора Transformer​

  1. В том же окне настроек перейдите на вкладку Классификатор.
  2. В пункте Алгоритм классификатора выберите Transformer.
  3. Нажмите Сохранить.

3. Использование транслита в обучающих фразах​

примечание

Примеры скриптов в этом разделе написаны на Python.

  1. В проекте перейдите в NLU → Интенты.
  2. Создайте или загрузите интенты, где часть тренировочных фраз написана транслитом. Т.е. часть фраз там написана не казахским алфавитом, а с заменой букв на русские/английские.

Тренировочные фразы​

Скрипт для создания тренировочных фраз с транслитерацией
  # Cоздаем словарь, в котором каждой казахской букве сопоставлен соответствующий список русских букв
translit_dic = {
'а': ['а', 'ау'],
'ә': ['ә', 'а', 'е'],
'б': ['б'],
'в': ['в'],
'г': ['г'],
'ғ': ['ғ', 'г'],
'д': ['д'],
'е': ['е', 'а', 'и', 'э'],
'ё': ['ё', 'е'],
'ж': ['ж'],
'з': ['з'],
'и': ['и','е'],
'й': ['й', 'и'],
'к': ['к'],
'қ': ['қ', 'к', 'х'],
'л': ['л'],
'м': ['м'],
'н': ['н'],
'ң': ['ң', 'нг', 'н'],
'о': ['о'],
'ө': ['ө', 'о', 'ё'],
'п': ['п'],
'р': ['р'],
'с': ['с'],
'т': ['т'],
'у': ['у'],
'ұ': ['ұ', 'у', 'о'],
'ү': ['ю', 'у'],
'ф': ['ф'],
'х': ['х'],
'һ': ['һ', 'х'],
'ц': ['ц'],
'ч': ['ч'],
'ш': ['ш', 'ч'],
'щ': ['щ'],
'ъ': ['ъ'],
'ы': ['ы', ''],
'і': ['і', 'и', 'ы', 'е', ''],
'ь': ['ь'],
'э': ['э', 'е'],
'ю': ['ю', 'йу'],
'я': ['я', 'йа'],
'ей': ['и'],
'-': ['-', ' ', ''],
' ': [' ', '']
}

# Функция принимает входное ключевое слово на казахском и генерирует все возможные варианты транслитерации для этого слова
def generate_transliterations(input_word):
# Если входное слово пустое, функция возвращает список из пустой строки
if not input_word:
return ''

# Если первая буква входного слова находится в словаре translit_dict, функция начинает генерировать транслитерации для этой буквы
results = ['']
text = input_word.lower()
i = 0

while i < len(text):
char = text[i]

if i < len(text) - 1:
char_pair = text[i:i+2]

if char_pair[0] == char_pair[1]: # Проверка на дублирование согласных
new_results = []

for result in results:
new_results.append(result)
new_results.append(result + char)

results = new_results
i += 1

if char_pair in translit_dic: # Проверка на соответствие пары символов одному символу
new_results = []
term_result = ''

for result in results:

for ru_char in translit_dic[char_pair]:
new_results.append(result + ru_char)

for ru_char in translit_dic[char_pair[0]]:
term_result = result + ru_char

for ru_char in translit_dic[char_pair[1]]:
new_results.append(term_result + ru_char)

results = new_results
i += 1

else:
if char in translit_dic:
new_results = []

for result in results:

for ru_char in translit_dic[char]:
new_results.append(result + ru_char)

results = new_results

else:
new_results = []

for result in results:
new_results.append(result + char)

results = new_results

else:
if char in translit_dic:
new_results = []

for result in results:

for ru_char in translit_dic[char]:
new_results.append(result + ru_char)

results = new_results

else:
new_results = []

for result in results:
new_results.append(result + char)

results = new_results

i += 1

return results

Воспользуемся этим скриптом для генерации транслитерации набора слов.

Генерируем транслитерации
from kz_transliterator import generate_transliterations

# Через запятую пишем слова, для которых хотим создать транслитерацию
input_keywords = 'жоқ,Иә,жатыр,түсті'

# Проходимся по каждому слову в списке и создаем для него транслитерацию
for word in input_keywords.split(','):
transliterations = generate_transliterations(word)
print(f'{word}: {transliterations}.')
print(f'Число вариантов транслитераций: {len(transliterations)}.\n')

# Получаем такой результат
жоқ: ['жоқ', 'жок', 'жох'].
Число вариантов транслитераций: 3.

Иә: ['иә', 'иа', 'ие', 'еә', 'еа', 'ее'].
Число вариантов транслитераций: 6.

жатыр: ['жатыр', 'жатр', 'жаутыр', 'жаутр'].
Число вариантов транслитераций: 4.

түсті: ['тюсті', 'тюсти', 'тюсты', 'тюсте', 'тюст', 'тусті', 'тусти', 'тусты', 'тусте', 'туст'].
Число вариантов транслитераций: 10.

Теперь вы можете создать и импортировать список интентов, используя в них сгенерированные транслитерации.

Создание регулярного выражения​

Если вам все еще не хватает точности распознавания или срабатывают не все интенты, то напишите регулярные выражения для наиболее популярных слов проекта в специальный паттерн. Укажите стандартное написание в качестве значения. Регулярное выражение, поймавшее один из таких паттернов, передаст дальше слово в стандартном виде.

Скрипт для создания регулярного выражения с транслитерацией
# Cоздаем словарь, в котором каждой казахской букве сопоставлен соответствующий список русских букв
translit_dic = {
'а': ['а', 'ау'],
'ә': ['ә', 'а', 'е'],
'б': ['б'],
'в': ['в'],
'г': ['г'],
'ғ': ['ғ', 'г'],
'д': ['д'],
'е': ['е', 'а', 'и', 'э'],
'ё': ['ё', 'е'],
'ж': ['ж'],
'з': ['з'],
'и': ['и','е'],
'й': ['й', 'и'],
'к': ['к'],
'қ': ['қ', 'к', 'х'],
'л': ['л'],
'м': ['м'],
'н': ['н'],
'ң': ['ң', 'нг', 'н'],
'о': ['о'],
'ө': ['ө', 'о', 'ё'],
'п': ['п'],
'р': ['р'],
'с': ['с'],
'т': ['т'],
'у': ['у'],
'ұ': ['ұ', 'у', 'о'],
'ү': ['ю', 'у'],
'ф': ['ф'],
'х': ['х'],
'һ': ['һ', 'х'],
'ц': ['ц'],
'ч': ['ч'],
'ш': ['ш', 'ч'],
'щ': ['щ'],
'ъ': ['ъ'],
'ы': ['ы', ''],
'і': ['і', 'и', 'ы', 'е', ''],
'ь': ['ь'],
'э': ['э', 'е'],
'ю': ['ю', 'йу'],
'я': ['я', 'йа'],
'ей': ['и'],
'-': ['-', ' ', ''],
' ': [' ', '']
}

# Принимает входное ключевое слово на казахском как аргумент.
def generate_regexp(input_word):

# Если входное слово пустое, функция возвращает пустую строку
if not input_word:
return ''

# Инициализируется переменная regexp_pattern, которая будет использоваться для построения регулярного выражения
regexp_pattern = '$regexp<'
text = input_word.lower()
i = 0

while i < len(text):
char = text[i]

if i < len(text) - 1:
char_pair = text[i:i+2]

# Если символ (char) равен пробелу, то в regexp_pattern добавляется регулярное выражение для соответствия пробельным символам (нулю и более) = \s*
if char == ' ':
regexp_pattern += r'\s*'
elif char == '-':
regexp_pattern += r'(-|\s)?'
else:

if char in translit_dic:

# Проверка на дублирование согласных
if char_pair[0] == char_pair[1]:
regexp_pattern += char + '{1,2}'
i += 1

# Проверка на соответствие пары символов одному символу
elif char_pair in translit_dic:

if '' in translit_dic[char_pair]:
regexp_pattern += '(' + '|'.join(translit_dic[char_pair][:-1]) + '?|'
else:
regexp_pattern += '(' + '|'.join(translit_dic[char_pair]) + '|'

if '' in translit_dic[char_pair[0]]:
regexp_pattern += '(' + '|'.join(translit_dic[char_pair[0][:-1]]) + ')?'
else:
regexp_pattern += '(' + '|'.join(translit_dic[char_pair[0]]) + ')'

if '' in translit_dic[char_pair[1]]:
regexp_pattern += '(' + '|'.join(translit_dic[char_pair[1][:-1]]) + '))?'
else:
regexp_pattern += '(' + '|'.join(translit_dic[char_pair[1]]) + '))'

i += 1

elif len(translit_dic[char]) > 1:

if '' in translit_dic[char]:
regexp_pattern += '(' + '|'.join(translit_dic[char][:-1]) + ')?'
else:
regexp_pattern += '(' + '|'.join(translit_dic[char]) + ')'


else:
regexp_pattern += char

else:
regexp_pattern += char

else:
if char in translit_dic:

# Если вариант транслитерации состоит из нескольких символов, они все добавляются
if len(translit_dic[char]) > 1:

if '' in translit_dic[char]:
regexp_pattern += '(' + '|'.join(translit_dic[char][:-1]) + ')?'
else:
regexp_pattern += '(' + '|'.join(translit_dic[char]) + ')'

# Если у символа только один вариант транслитерации, то этот вариант добавляется как строка
else:
regexp_pattern += ''.join(translit_dic[char])

# Если символ не имеет соответствия в словаре translit_dict, он просто добавляется в регулярное выражение
else:
regexp_pattern += char

i += 1

# После обработки всех символов входного слова, к regexp_pattern добавляется символ > для завершения регулярного выражения
regexp_pattern += '>'

return regexp_pattern

Используем этот скрипт, чтобы сгенерировать регулярные выражения для нескольких слов

Генерация регулярных выражений
from kz_transliterator import generate_regexp

# Через запятую пишем слова, для которых хотим создать транслитерацию
input_keywords = 'шешілмейді,Оператор ға,кредитімді,Ассаламағалейкум'

# Проходимся по каждому слову из списка
for word in input_keywords.split(','):
regexp_pattern = generate_regexp(word)
print(f'{word}: {regexp_pattern}.\n')

# Получаем такой результат
шешілмейді: $regexp<(ш|ч)(е|а|и|э)(ш|ч)(і|и|ы|е)?лм(и|(е|а|и|э)(й|и))д(і|и|ы|е)?>.

Оператор ға: $regexp<оп(е|а|и|э)р(а|ау)тор\s*(ғ|г)(а|ау)>.

кредитімді: $regexp<кр(е|а|и|э)д(и|е)т(і|и|ы|е)?мд(і|и|ы|е)?>.

Ассаламағалейкум: $regexp<(а|ау)с{1,2}(а|ау)л(а|ау)м(а|ау)(ғ|г)(а|ау)л(и|(е|а|и|э)(й|и))кум>.

Теперь мы можем использовать эти сгенерированные регулярные выражения в preMatch, чтобы приводить слова к стандартной казахской орфографии.

require: common.js
module = sys.zb-common

patterns:
$kz_trans = (
$regexp<<(ш|ч)(е|а|и|э)(ш|ч)(і|и|ы|е)?лм(и|(е|а|и|э)(й|и))д(і|и|ы|е)?>:шешілмейді
| $regexp<оп(е|а|и|э)р(а|ау)тор\s*(ғ|г)(а|ау)>:Оператор ға
| $regexp<кр(е|а|и|э)д(и|е)т(і|и|ы|е)?мд(і|и|ы|е)?>:кредитімді
| $regexp<(а|ау)с{1,2}(а|ау)л(а|ау)м(а|ау)(ғ|г)(а|ау)л(и|(е|а|и|э)(й|и))кум>:Ассаламағалейкум
)
$kz = * $kz_trans *

init:
bind("preMatch", function($context) {
// В preMatch заменяем слова, попавшие в регулярное выражение на стандартное написание
var match = $nlp.matchPatterns($.request.query, ["* $repeat<$kz> *"]);
if (match && match.parseTree && match.parseTree.kz) {
var query = $.request.query;
var transliterations = match.parseTree.kz.map(function(pattern) {
return pattern.kz_trans && pattern.kz_trans[0];
});
transliterations.forEach(function(pattern) {
query = query.replace(pattern.text, pattern.value);
});
// Редактируем запрос — далее обрабатываться будет уже исправленная версия
$context.request.query = query;
}
});

theme: /

state: Start
q!: $regex</start>
a: Начнём.

state: NoMatch
event!: noMatch
a: Я не понял. Вы сказали: {{$request.query}}