Транслитерация в текстовом канале
В этой статье мы настроим бота, чтобы он мог понимать транслитерацию в казахском языке. Бот будет работать в текстовом канале.
В этом языке есть буквы, которых нет на стандартной клавиатуре с английской и русской раскладкой.
А мы знаем, что часть пользователей предпочитает именно такие клавиатуры.
Например, им приходится вместо иә
писать еэ
или йэ
.
Такой бот сможет правильно считывать казахские слова, написанные другой раскладкой.
Решение
Для наибольшей эффективности применим следующую последовательность действий:
- Установить тот системный язык, который нужно распознавать. В нашем случае казахский.
- Выбрать классификатора Transformer.
- Добавить обучающие фразы, которые содержат транслитерацию.
- Создать регулярные выражения.
1. Установка системного языка
- Перейдите на главную страницу JAICP и найдите нужный проект в секции Мои проекты.
- Нажмите в карточке проекта и выберите Настройки проекта.
- В поле Язык NLU выберите казахский.
2. Использование классификатора Transformer
- В том же окне настроек перейдите на вкладку Классификатор.
- В пункте Алгоритм классификатора выберите Transformer.
- Нажмите Сохранить.
3. Использование транслита в обучающих фразах
Примеры скриптов в этом разделе написаны на Python.
- В проекте перейдите в NLU → Интенты.
- Создайте или загрузите интенты, где часть тренировочных фраз написана транслитом. Т.е. часть фраз там написана не казахским алфавитом, а с заменой букв на русские/английские.
Тренировочные фразы
Скрипт для создания тренировочных фраз с транслитерацией
# 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}}