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

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

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

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

Решение

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

  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}}