この記事の通りにやったらできるだろと思ってたら案外苦戦したのでこの部分だけ切り出し。
結果としては
testの指定が書いてないバージョンでこける。ので-x test記載。
./gradlew -PengineVersion=os:2.13.0 build -x test
ってことだった。
dockerでマルチステージビルドするのがスムーズなので辞書のダウンロード含めて書いてしまうのがよき。
# ビルドステージ
FROM gradle:jdk17 as build-stage
RUN git clone https://github.com/WorksApplications/elasticsearch-sudachi.git
WORKDIR elasticsearch-sudachi
RUN git checkout v3.1.0
# OpenSearch (os), v2.13.0 でビルド。testの指定が書いてないバージョンでこける。ので-x test記載。
RUN ./gradlew -PengineVersion=os:2.13.0 build -x test
# ここからダウンロード
# 20240428時点最新がこれ。
# coreは必須。
RUN wget http://sudachi.s3-website-ap-northeast-1.amazonaws.com/sudachidict/sudachi-dictionary-20240409-core.zip
RUN unzip sudachi-dictionary-20240409-core.zip sudachi-dictionary-20240409/system_core.dic
# fullもあとでつかいたくなるので書いてあるがなくていい。
RUN wget http://sudachi.s3-website-ap-northeast-1.amazonaws.com/sudachidict/sudachi-dictionary-20240409-full.zip
RUN unzip sudachi-dictionary-20240409-full.zip sudachi-dictionary-20240409/system_full.dic
# smallもあとでつかいたくなるので書いてあるがなくていい。
RUN wget http://sudachi.s3-website-ap-northeast-1.amazonaws.com/sudachidict/sudachi-dictionary-20240409-small.zip
RUN unzip sudachi-dictionary-20240409-small.zip sudachi-dictionary-20240409/system_small.dic
# ここでユーザー辞書のビルドもかいておく
# ランタイムステージ
FROM opensearchproject/opensearch:2.13.0
# ビルドステージからプラグインをコピー
COPY --from=build-stage /home/gradle/elasticsearch-sudachi/build/distributions/opensearch-2.13.0-analysis-sudachi-3.1.0.zip .
# 辞書をコピーする
COPY --from=build-stage /home/gradle/elasticsearch-sudachi/sudachi-dictionary-20240409/system_core.dic config/sudachi/system_core.dic
COPY --from=build-stage /home/gradle/elasticsearch-sudachi/sudachi-dictionary-20240409/system_full.dic config/sudachi/system_full.dic
COPY --from=build-stage /home/gradle/elasticsearch-sudachi/sudachi-dictionary-20240409/system_small.dic config/sudachi/system_small.dic
# プラグインをファイルからインストールする
RUN /usr/share/opensearch/bin/opensearch-plugin install --batch file:opensearch-2.13.0-analysis-sudachi-3.1.0.zip
RUN opensearch-plugin install analysis-kuromoji
いえい。
形態素解析
フィルターとかの詳細は
https://github.com/WorksApplications/elasticsearch-sudachi/tree/develop
このへんに詳しい。
正規化とsearchモード組み合わせが基本的には強い。
PUT sudachi-index-example
{
"settings" : {
"analysis" : {
"filter" : {
"search" : {
"type" : "sudachi_split",
"mode" : "search"
},
"synonym" : {
"type" : "synonym",
"synonyms" : [ "関西国際空港,関空", "関西 => 近畿" ]
},
"romaji_readingform" : {
"type" : "sudachi_readingform",
"use_romaji" : true
},
"katakana_readingform" : {
"type" : "sudachi_readingform",
"use_romaji" : false
}
},
"analyzer" : {
"sudachi_baseform_analyzer" : {
"filter" : [ "sudachi_baseform" ],
"type" : "custom",
"tokenizer" : "sudachi_tokenizer"
},
"sudachi_normalizedform_analyzer" : {
"filter" : [ "sudachi_normalizedform" ],
"type" : "custom",
"tokenizer" : "sudachi_tokenizer"
},
"sudachi_readingform_analyzer" : {
"filter" : [ "katakana_readingform" ],
"type" : "custom",
"tokenizer" : "sudachi_tokenizer"
},
"sudachi_romaji_analyzer" : {
"filter" : [ "romaji_readingform" ],
"type" : "custom",
"tokenizer" : "sudachi_tokenizer"
},
"sudachi_search_analyzer" : {
"filter" : [ "search" ],
"type" : "custom",
"tokenizer" : "sudachi_tokenizer"
},
"sudachi_search_analyzer2" : {
"filter" : [ "search" ,"sudachi_normalizedform"],
"type" : "custom",
"tokenizer" : "sudachi_tokenizer"
},
"sudachi_synonym_analyzer" : {
"filter" : [ "synonym", "search" ],
"type" : "custom",
"tokenizer" : "sudachi_tokenizer"
},
"sudachi_analyzer" : {
"filter" : [],
"type": "custom",
"tokenizer": "sudachi_tokenizer"
},
"sudachi_a_analyzer" : {
"filter" : [],
"type" : "custom",
"tokenizer" : "sudachi_a_tokenizer"
}
},
"tokenizer" : {
"sudachi_tokenizer": {
"type": "sudachi_tokenizer",
"split_mode": "C"
},
"sudachi_a_tokenizer": {
"type": "sudachi_tokenizer",
"split_mode": "A"
}
}
}
},
"mappings": {
"properties": {
"本文": {
"type": "text",
"analyzer": "sudachi_search_analyzer2"
}
}
}
}
とりあえず…例がたくさんついてて見にくいけど、sudachiで形態素解析するindex。本文にsudachi_search_analyzer2で解析してる。
"sudachi_search_analyzer2" : {
"filter" : [ "search" ,"sudachi_normalizedform"],
"type" : "custom",
"tokenizer" : "sudachi_tokenizer"
},
これはsearchモードとsudachi_normalizedformで正規化してるので検索。
POST _bulk
{"index": {"_index": "sudachi-index-example", "_id": "1"}}
{"本文": "Qiitaで記事書くの久しぶりかも。個人的には雑な記事で許される雰囲気が好き。"}
{"index": {"_index": "sudachi-index-example", "_id": "2"}}
{"本文": "テストデータです。"}
{"index": {"_index": "sudachi-index-example", "_id": "3"}}
{"本文": "ねむいよ。"}
{"index": {"_index": "sudachi-index-example", "_id": "4"}}
{"本文": "サンプルデータで検索引っかかりやすさの差が出るデータを作るのがめんどくさい。"}
{"index": {"_index": "sudachi-index-example", "_id": "5"}}
{"本文": "本日は晴天なり"}
{"index": {"_index": "sudachi-index-example", "_id": "6"}}
{"本文": "すっごくどうでもいいけど、「アイマリンプロジェクト 「Dive to Blue」 MMD MUSIC VIDEO」がマイブームです。https:\/\/www.youtube.com/watch?v=XCzGs6rLf4s"}
とりあえずいつもの?これ。
GET sudachi-index-example/_search?q=プロジェクト
{
"took": 16,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 0,
"relation": "eq"
},
"max_score": null,
"hits": []
}
}
結果は…ゼロ件orz。これはkuromojiでもゼロ件なんですが、形態素解析の都合で「アイマリンプロジェクト」が固有名詞っぽく(実際固有名詞)、「アイマリンプロジェクト」とされてしまっているせいです。「アイ」「マリン」「プロジェクト」と「アイマリンプロジェクト」それぞれが転置マッピングされててほしいわけなんだが…。この辺はユーザー辞書が欲しい。
今どきだったらもうハイブリッド検索でembeddingをちょっと混ぜるとかのほうが安定はするかもしれないね。
ちなみに正規化が効くのはこういう話。
POST _bulk
{"index": {"_index": "sudachi-index-example"}}
{"本文": "デンプンってでんぷんとか澱粉とかでん粉とか表記ゆれが多いよね"}
{"index": {"_index": "sudachi-index-example"}}
{"本文": "デンプンって表記ゆれが多いよね"}
{"index": {"_index": "sudachi-index-example"}}
{"本文": "でんぷんって表記ゆれが多いよね"}
{"index": {"_index": "sudachi-index-example"}}
{"本文": "でん粉って表記ゆれが多いよね"}
{"index": {"_index": "sudachi-index-example"}}
{"本文": "澱粉って表記ゆれが多いよね"}
GET sudachi-index-example/_search?q=でんぷん
ひらがなで「でんぷん」ってキーワード入れてても「でんぷん」「澱粉」「デンプン」「でん粉」全部ひっかかります。なぜならば辞書側で正規化してみんな「澱粉」で登録されてるから。
{
"took": 3,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 5,
"relation": "eq"
},
"max_score": 1.2181944,
"hits": [
{
"_index": "sudachi-index-example",
"_id": "Oy2NII8BjEjOixjkjdRN",
"_score": 1.2181944,
"_source": {
"本文": "デンプンってでんぷんとか澱粉とかでん粉とか表記ゆれが多いよね"
}
},
{
"_index": "sudachi-index-example",
"_id": "PC2NII8BjEjOixjkjdRN",
"_score": 0.8899586,
"_source": {
"本文": "デンプンって表記ゆれが多いよね"
}
},
{
"_index": "sudachi-index-example",
"_id": "PS2NII8BjEjOixjkjdRN",
"_score": 0.8899586,
"_source": {
"本文": "でんぷんって表記ゆれが多いよね"
}
},
{
"_index": "sudachi-index-example",
"_id": "Pi2NII8BjEjOixjkjdRN",
"_score": 0.8899586,
"_source": {
"本文": "でん粉って表記ゆれが多いよね"
}
},
{
"_index": "sudachi-index-example",
"_id": "Py2NII8BjEjOixjkjdRN",
"_score": 0.8899586,
"_source": {
"本文": "澱粉って表記ゆれが多いよね"
}
}
]
}
}
前回のindexに入れてやって試すとこんな感じ
POST my-nlp-e5/_search?search_pipeline=nlp-search-pipeline
{
"_source": {
"exclude": [
"embedding"
]
},
"query": {
"match": {
"text": {
"query": "でんぷん"
}
}
}
}
kuromojiだけの結果。
{
"took": 13,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 2,
"relation": "eq"
},
"max_score": 1.8653811,
"hits": [
{
"_index": "my-nlp-e5",
"_id": "SC2OII8BjEjOixjkptTa",
"_score": 1.8653811,
"_source": {
"text": "でんぷんって表記ゆれが多いよね"
}
},
{
"_index": "my-nlp-e5",
"_id": "Ri2OII8BjEjOixjkptTa",
"_score": 1.525284,
"_source": {
"text": "デンプンってでんぷんとか澱粉とかでん粉とか表記ゆれが多いよね"
}
}
]
}
}
「でんぷん」表記があるのだけでてきました。
ちなみにハイブリッドだと
POST my-nlp-e5/_search?search_pipeline=nlp-search-pipeline
{
"_source": {
"exclude": [
"embedding"
]
},
"query": {
"hybrid": {
"queries": [
{
"match": {
"text": {
"query": "でんぷん"
}
}
},
{
"neural": {
"embedding": {
"query_text": "でんぷん",
"model_id": "SAS3FY8Bu7RVyOi7jDQ2",
"k": 5
}
}
}
]
}
}
}
ぜんぶでてきます
{
"took": 275,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 5,
"relation": "eq"
},
"max_score": 1,
"hits": [
{
"_index": "my-nlp-e5",
"_id": "SC2OII8BjEjOixjkptTa",
"_score": 1,
"_source": {
"text": "でんぷんって表記ゆれが多いよね"
}
},
{
"_index": "my-nlp-e5",
"_id": "Ri2OII8BjEjOixjkptTa",
"_score": 0.49498686,
"_source": {
"text": "デンプンってでんぷんとか澱粉とかでん粉とか表記ゆれが多いよね"
}
},
{
"_index": "my-nlp-e5",
"_id": "SS2OII8BjEjOixjkptTa",
"_score": 0.3487706,
"_source": {
"text": "でん粉って表記ゆれが多いよね"
}
},
{
"_index": "my-nlp-e5",
"_id": "Ry2OII8BjEjOixjkptTa",
"_score": 0.02692861,
"_source": {
"text": "デンプンって表記ゆれが多いよね"
}
},
{
"_index": "my-nlp-e5",
"_id": "Si2OII8BjEjOixjkptTa",
"_score": 0.00070000003,
"_source": {
"text": "澱粉って表記ゆれが多いよね"
}
}
]
}
}
sudachiでアイマリンプロジェクトをプロジェクトとかマリンでひっかかるようにしたいなー