Недавно я создал японский поисковый сервис на основе Elastic Stack и обнаружил, что японский поиск имеет много особенностей по сравнению с английским и китайским, поэтому я записал его.Создание японской поисковой системы с помощью Elasticsearchнекоторые моменты. Все примеры в этой статье относятся к версиям Elastic 6.X и 7.X.
Специфика японского поиска
Возьмем в качестве примера вводный язык Elastic «Elasticsearch は, ожидаемые し た результаты や, そ う で な い も の も 検 so で き る よ う に デ ー タ を 集 め て す る Elastic Stack コ ア». Как поисковая система, мы, конечно, надеемся, что пользователи смогут искать этот результат по всем основным ключевым словам в предложении.
Как и в английском языке, японские глаголы изменяются по-разному в зависимости от времени, контекста и т. д. Например, «сбор めて» в примере означает настоящее продолженное время, которое относится к форме て конъюнктивной формы глагола, а его терминальная форма (которую можно понимать как прототип глагола) — «сборめる". Японский глагол может иметь более 10 спряжений. Если вы полагаетесь на простую сегментацию слов, пользователи не смогут сопоставить это предложение при поиске «集 め る».
В дополнение к глаголам, японские прилагательные также имеют склонения.Например, окончательная форма «安い» может иметь различные изменения, такие как конъюнктивная форма «安く», непредсказуемая «安かろ» и фиксированная форма «安けれ».
Как и в китайском, в японском есть многосложные слова, особенно имена людей, географические названия и т. Д. Например, «сянъюй» имеет различное произношение, такое как сагара, сораку, саганака, когда это имя человека и географическое название.
В то же время в японском языке есть разные варианты написания слова, например «пустой 玶» = «пустой き玶».
Для поисковой системы завершение ввода также является очень важной частью. Судя по японскому способу ввода, при поиске пользователи могут вводить в различных формах, и есть следующие возможности:
- Хирагана, например "検so -> けんさく"
- Полная ширина катаканы, например «検so -> ケンサク».
- Катакана половинной ширины, например «検so -> ケンサク»
- Китайские иероглифы, такие как «検so»
- Полноразмерные латинские символы, такие как «検so -> kennsaku».
- Полуширинный латинский символ, например «検so -> kennsaku».
и т.п. Это похоже на китайский пиньинь.Когда пользователи ищут результаты или завершают ввод, мы также надеемся максимально адаптироваться к привычкам пользователя, чтобы улучшить взаимодействие с пользователем.
Процесс индексации текста Elasticsearch
Elasticsearch (далее ES), как относительно зрелая поисковая система, имеет некоторые решения вышеуказанных проблем.
Давайте сначала рассмотрим процесс, который будет проходить текст ЭП при индексировании.При сохранении текста в поле (Поле) можно указать уникальный анализатор (Анализатор), функция анализатора - фильтровать, деформировать и сегментировать текст. исходный текст, другими словами, он преобразуется в элемент слова (термин), который может быть найден ES, чтобы установить индекс, то есть:
graph LR
Text --> Analyzer
Analyzer --> Term
Внутри анализатора он состоит из 3 частей
- Фильтр символов: выполнять фильтрацию символов в тексте, например, обрабатывать символы HTML-тегов в тексте. Анализатор может содержать 0 или более символьных фильтров, которые обрабатываются в порядке конфигурации.
- Токенизатор: Маркировать текст. Анализатор должен содержать только один токенизатор.
- Фильтр токенов: фильтрация слов, разделенных токенизатором. Например, строчные буквы, остановка обработки слов, обработка синонимов и т. д. Анализатор может содержать 0 или более фильтров терминов, несколько в порядке конфигурации.
Цитата из картинок должна быть более яркой
ЕС имеетНекоторые анализаторы встроены в, но очевидно, что для более сложных сценариев, таких как поиск на японском языке, обычно необходимо создать собственный анализатор в соответствии с потребностями.
Кроме того, ЕС имеетнормализаторыКонцепцию , можно понимать как многоразовые анализаторы, например, все наши данные взяты с веб-страниц на английском языке, символы html на веб-страницах, замена специальных символов и т. д. в основном одинаковы, чтобы избежать этих общих Обработка определяется один раз в каждом анализаторе и может быть индивидуально отсортирована в нормализаторе.
Анализатор быстрых тестов
Для достижения хорошего эффекта поиска конфигурация Анализатора, несомненно, будет корректироваться различными способами.Для большей эффективности следует сначала освоить метод быстрого тестирования Анализатора.Подробности см. в этой части.Как быстро протестировать Elasticsearch Analyzer, которые здесь повторяться не будут.
Сравнение и выбор японских токенизаторов Elasticsearch
Сегментация японских слов — относительно большая тема, поэтому была открыта отдельная статья.Представление и сравнение основных проектов сегментации японских слов с открытым исходным кодом. процитировать окончательный вывод
Алгоритмы/Модели | язык реализации | Словарь | скорость обработки | ES-плагин | Lisence | |
---|---|---|---|---|---|---|
MeCab | CRF | C++ | по желанию | Самый высокий | имеют | GPL/LGPL/BSD |
Kuromoji | Viterbi | Java | необязательный, ipadic по умолчанию | середина | встроенный | Apache License v2.0 |
Juman++ | RNNLM | C++ | ручной работы | высокий | никто | Apache License v2.0 |
KyTea | СВМ и др. | C++ | UniDic | середина | имеют | Apache License v2.0 |
Sudachi | Lattice LSTM | Java | UniDic + NEologd | середина | имеют | Apache License v2.0 |
nagisa | Bi-LSTM | Python | ipadic | Низкий | никто | MIT |
Для Elasticsearch на ранней стадии проекта из-за нехватки данных нет четкой цели по оптимизации результатов поиска, рекомендуется использовать непосредственно Kuromoji или Sudachi, которые просты в установке и обладают полным функционалом. На средней и поздней стадиях проекта, с учетом оптимизации качества и эффективности сегментации слов, его можно заменить на MeCab или Juman++. В этой статье в качестве примера будет использоваться Куромодзи.
Фильтр похожих токенов поиска на японском языке
На основе того, что Tokenizer был определен, другие оптимизации поиска на японском языке выполняются с помощью фильтра Token, включая встроенный фильтр Token ES и фильтр Token, прикрепленный к Kuromoji, которые представлены один за другим ниже.
Фильтр токенов нижнего регистра
Преобразование английского в нижний регистр, обычная настройка практически для большинства поисковых запросов.
POST _analyze
{
"tokenizer": "kuromoji_tokenizer",
"filter": ["lowercase"],
"text": "Ironman"
}
Response
{
"tokens": [
{
"token": "ironman",
"start_offset": 0,
"end_offset": 7,
"type": "word",
"position": 0
}
]
}
Фильтр токена ширины CJK
Преобразование символов ASCII полной ширины в символы ASCII половинной ширины
POST _analyze
{
"tokenizer": "kuromoji_tokenizer",
"filter": ["cjk_width"],
"text": "kennsaku"
}
{
"tokens": [
{
"token": "kennsaku",
"start_offset": 0,
"end_offset": 8,
"type": "word",
"position": 0
}
]
}
а также преобразование катаканы половинной ширины в полную ширину
POST _analyze
{
"tokenizer": "kuromoji_tokenizer",
"filter": ["cjk_width"],
"text": "ケンサク"
}
{
"tokens": [
{
"token": "ケンサク",
"start_offset": 0,
"end_offset": 4,
"type": "word",
"position": 0
}
]
}
ja_stop
Token Filter (фильтр японских стоп-слов)
Вообще говоря, японские стоп-слова в основном включают в себя некоторые вспомогательные слова, вспомогательные глаголы, союзы и знаки препинания и т. д. Стоп-слова, используемые Куромодзи по умолчанию, относятся кИсходный код стоп-слова Lucene на японском языке. Исходя из этого, вы также можете сами добавлять стоп-слова в конфигурацию
POST _analyze
{
"tokenizer": "kuromoji_tokenizer",
"filter": ["ja_stop"],
"text": "Kuromojiのストップワード"
}
{
"tokens": [
{
"token": "Kuromoji",
"start_offset": 0,
"end_offset": 8,
"type": "word",
"position": 0
},
{
"token": "ストップ",
"start_offset": 9,
"end_offset": 13,
"type": "word",
"position": 2
},
{
"token": "ワード",
"start_offset": 13,
"end_offset": 16,
"type": "word",
"position": 3
}
]
}
kuromoji_baseform
Token Filter (фильтр японского корня)
Преобразование глаголов, прилагательных в корень слова
POST _analyze
{
"tokenizer": "kuromoji_tokenizer",
"filter": ["kuromoji_baseform"],
"text": "飲み"
}
{
"tokens": [
{
"token": "飲む",
"start_offset": 0,
"end_offset": 2,
"type": "word",
"position": 0
}
]
}
kuromoji_readingform
Token Filter (фильтр японского произношения)
Преобразование слов в произношение, произношение может быть 2 формы катакана или романджи
POST _analyze
{
"tokenizer": "kuromoji_tokenizer",
"filter": ["kuromoji_readingform"],
"text": "寿司"
}
{
"tokens": [
{
"token": "スシ",
"start_offset": 0,
"end_offset": 2,
"type": "word",
"position": 0
}
]
}
POST _analyze
{
"tokenizer": "kuromoji_tokenizer",
"filter": [{
"type": "kuromoji_readingform", "use_romaji": true
}],
"text": "寿司"
}
{
"tokens": [
{
"token": "sushi",
"start_offset": 0,
"end_offset": 2,
"type": "word",
"position": 0
}
]
}
Когда встречаются полифоны, фильтрация произношения дает только одно произношение.
kuromoji_part_of_speech
Token Filter (японский модальный фильтр частиц)
Существует определенное перекрытие между фильтрацией модальных частиц и фильтрацией стоп-слов, а объем фильтрации модальных частиц шире. Объектом фильтрации стоп-слов является фиксированный список слов, а фильтрация стоп-слов фильтруется по частям речи.исходный код.
POST _analyze
{
"tokenizer": "kuromoji_tokenizer",
"filter": ["kuromoji_part_of_speech"],
"text": "寿司がおいしいね"
}
{
"tokens": [
{
"token": "寿司",
"start_offset": 0,
"end_offset": 2,
"type": "word",
"position": 0
},
{
"token": "おいしい",
"start_offset": 3,
"end_offset": 7,
"type": "word",
"position": 2
}
]
}
kuromoji_stemmer
Token Filter (японский фильтр длинных тонов)
Удалите длинные звуки в конце некоторых слов, например, "コンピューター" => "コンピュータ"
POST _analyze
{
"tokenizer": "kuromoji_tokenizer",
"filter": ["kuromoji_stemmer"],
"text": "コンピューター"
}
{
"tokens": [
{
"token": "コンピュータ",
"start_offset": 0,
"end_offset": 7,
"type": "word",
"position": 0
}
]
}
kuromoji_number
Token Filter (фильтр японских номеров)
Преобразование китайских чисел в числа ASCII
POST _analyze
{
"tokenizer": "kuromoji_tokenizer",
"filter": ["kuromoji_number"],
"text": "一〇〇〇"
}
{
"tokens": [
{
"token": "1000",
"start_offset": 0,
"end_offset": 4,
"type": "word",
"position": 0
}
]
}
Конфигурация анализатора полнотекстового поиска на японском языке
На основе вышеперечисленных компонентов несложно придумать полноценную конфигурацию анализатора полнотекстового поиска на японском языке.
PUT my_index
{
"settings": {
"analysis": {
"analyzer": {
"ja_fulltext_analyzer": {
"type": "custom",
"tokenizer": "kuromoji_tokenizer",
"filter": [
"cjk_width",
"lowercase",
"kuromoji_stemmer",
"ja_stop",
"kuromoji_part_of_speech",
"kuromoji_baseform"
]
}
}
}
},
"mappings": {
"my_type": {
"properties": {
"title": {
"type": "text",
"analyzer": "ja_fulltext_analyzer"
}
}
}
}
}
На самом деле этоkuromoji
analyzerИспользуемая конфигурация, поэтому приведенное выше эквивалентно
PUT my_index
{
"mappings": {
"my_type": {
"properties": {
"title": {
"type": "text",
"analyzer": "kuromoji"
}
}
}
}
}
Такие настройки по умолчанию уже могут справиться с общей ситуацией.Основная проблема использования настроек по умолчанию заключается в том, что словарь не отшлифован, а сегментация слов некоторых новых слов или профессиональных полей неточна.Например, ожидаемый результат сегментации слов «Токио スカイツリー» — это «Токио/スカイツリー», фактический результат сегментации слова — «Токио/スカイ/ツリー». В свою очередь, некоторые поисковые рейтинги не идеальны. Эту проблему можно решить, переключив словарь на UniDic + NEologd, который может охватывать больше новых слов и сетевых терминов. В то же время также необходимо постоянно поддерживать собственный словарь в соответствии с поисковыми запросами пользователей. Пользовательский словарь также может решить проблему многократного написания и многосложных слов.
Что касается проблемы соответствия произношения кана, упомянутой в начале этой статьи, легко подумать о добавленииkuromoji_readingform
, чтобы окончательный сохраненный Term индекса был в форме псевдонима, что действительно может решить проблему ввода псевдонима, но это вызовет новые проблемы:
с одной стороны,kuromoji_readingform
Преобразованное произношение кана не обязательно является точным, особенно если встречаются некоторые необычные варианты написания, такие как «明るい» -> «アカルイ» правильно, «明るい» правильно.отправить りがなНаписание «明かるい» будет преобразовано в неправильное «メイ・カルイ».
С другой стороны, очень часто один и тот же псевдоним в японском языке соответствует разным китайским иероглифам, например, «シアワセ» может быть записано как «счастливый», «шихэわせ» и так далее.
следовательноkuromoji_readingform
Он не подходит для большинства сценариев, его можно добавить по мере необходимости при вводе завершения и поиске имен людей и мест с известным произношением.
Реализация японского автодополнения
В Elasticsearch есть 4 типа подсказок: подсказка терминов и подсказка фраз основаны на вводе для поиска похожих слов или фраз, в основном используются для исправления ошибок ввода, общий сценарий — «Вы ищете XXX»; подсказка контекста — личное понимание. обычно используется для добавления уточнений к другим полям для автозаполнения, что эквивалентно фильтру в запросе, поэтому здесь представлен наиболее часто используемый вариант дополнения.
Completion Suggester должен реагировать на каждый введенный символ, что требует очень высокой производительности, поэтому ES использует для этого новую структуру данных:FST полностью загружен в память (In Memory FST), типcompletion
. Как мы все знаем, тип данных ES в основном использует обратный индекс, но из-за большого количества данных терминов вводятся словарь терминов и указатель терминов, поэтому поисковый запрос будет проходить следующий процесс.
graph LR
TI[Term Index]
TD[Term Dictionary]
PL[Posting List]
Query --> TI
TI --> TD
TD --> Term
Term --> PL
PL --> Documents
Завершение пропускает словарь терминов и индекс терминов, не требует объединения результатов из нескольких узлов и использует только память для завершения вычислений, поэтому производительность очень высока. Но поскольку используется только одна структура данных FST, может быть реализован только префиксный поиск.
Имея это в виду, давайте рассмотрим, как создать автозаполнение для японского языка.
В отличие от полнотекстового поиска, при автодополнении очень сильно требуется совпадение произношения и латинских символов, например, пользователь вводит «Гинтама». В соответствии с порядком ввода пользователя фактически сгенерированные символы должны быть
- gin
- ぎん
- Серебряный
- серебро т
- серебряная тама
- Гинтама
В идеале все вышеперечисленные входы должны быть сопоставлены с «Gintama», так как же добиться такого автоматического завершения? Обычный метод заключается в подготовке одного поля для каждого из китайских иероглифов, кана и латинских символов, выполнении автозаполнения в 3 полях одновременно при вводе, а затем объединении заполненных результатов.
Рассмотрим практический пример: в установленном ниже индексе создаются два типа Token Filters.kuromoji_readingform
Может конвертировать текст в катакану,romaji_readingform
затем вы можете преобразовать текст в Romaji, использовать его сkuromoji
Комбинация анализаторов, вы получаете соответствующий пользовательский анализаторja_reading_analyzer
а такжеja_romaji_analyzer
.
Для поля заголовка используйте другой анализатор для индексации:
-
title
: тип текста, использованиеkuromoji
Анализатор для общего поиска по ключевым словам -
title.suggestion
: тип завершения, использованиеkuromoji
Анализатор для автодополнения с китайскими иероглифами -
title.reading
: тип завершения, использованиеja_reading_analyzer
Анализатор, автодополнение для псевдонимов -
title.romaji
: тип завершения, использованиеja_romaji_analyzer
Анализатор для автодополнения латинских символов
PUT my_index
{
"settings": {
"analysis": {
"filter": {
"katakana_readingform": {
"type": "kuromoji_readingform",
"use_romaji": "false"
},
"romaji_readingform": {
"type": "kuromoji_readingform",
"use_romaji": "true"
}
},
"analyzer": {
"ja_reading_analyzer": {
"type": "custom",
"filter": [
"cjk_width",
"lowercase",
"kuromoji_stemmer",
"ja_stop",
"kuromoji_part_of_speech",
"kuromoji_baseform",
"katakana_readingform"
],
"tokenizer": "kuromoji_tokenizer"
},
"ja_romaji_analyzer": {
"type": "custom",
"filter": [
"cjk_width",
"lowercase",
"kuromoji_stemmer",
"ja_stop",
"kuromoji_part_of_speech",
"kuromoji_baseform",
"romaji_readingform"
],
"tokenizer": "kuromoji_tokenizer"
}
}
}
},
"mappings": {
"my_type": {
"properties": {
"title": {
"type": "text",
"analyzer": "kuromoji",
"fields": {
"reading": {
"type": "completion",
"analyzer": "ja_reading_analyzer",
"preserve_separators": false,
"preserve_position_increments": false,
"max_input_length": 20
},
"romaji": {
"type": "completion",
"analyzer": "ja_romaji_analyzer",
"preserve_separators": false,
"preserve_position_increments": false,
"max_input_length": 20
},
"suggestion": {
"type": "completion",
"analyzer": "kuromoji",
"preserve_separators": false,
"preserve_position_increments": false,
"max_input_length": 20
}
}
}
}
}
}
}
Вставьте образцы данных
POST _bulk
{ "index": { "_index": "my_index", "_type": "my_type", "_id": 1} }
{ "title": "銀魂" }
Затем запустите автозаполненный запрос
GET my_index/_search
{
"suggest": {
"title": {
"prefix": "gin",
"completion": {
"field": "title.suggestion",
"size": 20
}
},
"titleReading": {
"prefix": "gin",
"completion": {
"field": "title.reading",
"size": 20
}
},
"titleRomaji": {
"prefix": "gin",
"completion": {
"field": "title.romaji",
"size": 20
}
}
}
}
Вы можете увидеть хиты разных входов
- джин: хит
title.romaji
- ぎん: хит
title.reading
а такжеtitle.romaji
- серебро: хит
title.suggestion
,title.reading
а такжеtitle.romaji
- серебро т: хит
title.romaji
- серебро たま: хит
title.reading
а такжеtitle.romaji
- Гинтама: Хит
title.suggestion
,title.reading
а такжеtitle.romaji