用 Elasticsearch + Sudachi + Docker 创建用户词典进行实操
这次我使用Elasticsearch + Sudachi创建了一个使用用户词典的Dockerfile,并分享了制作方法。Elasticsearch的版本是当前最新版本(v7.4.0),但已确认也可在v6.8左右版本运行。
Sudachi 是什么

Sudachi是一种日本语形态素解析器,由株式会社ワークスアプリケーションズ下属的ワークス徳島人工知能NLP研究所开发。它具有支持多个分割单位等特点。有关文档请点击此处:https://github.com/WorksApplications/Sudachi/#sudachi-日本语readme
这次实操的最终设计
最终目标是追求以下的构成。
.
├── docker-compose.yml
└── elasticsearch
├── Dockerfile
└── sudachi
├── README.md
├── custom_dict.txt
└── sudachi.json

自定义词典文本
首先,让我们创建一个用户自定义词典源文件custom_dict.txt,作为用户词典的基础。顺便说一下,文件名可以任意取,都可以。我们以我Twitter账号的「po3rin」为例,来看看如何将其添加到词典中。
po3rin,4786,4786,5000,po3rin,名詞,固有名詞,一般,*,*,*,po3rin,po3rin,*,*,*,*,*
关于格式的相关信息都已经整理在文档中,请参考此链接:
https://github.com/WorksApplications/Sudachi/blob/develop/docs/user_dict.md
当然,这种单词在字典中是找不到的,所以本来会被分解成下面的一些token。
{
"tokens" : [
{
"token" : "po",
// ...
}
{
"token" : "3",
// ...
}
{
"token" : "rin",
// ...
}
]
}
我们稍后来看看如果将”po3rin”添加到用户词典中会发生什么。
酸橙.json
接下来,我们需要准备一个文件,以便能够覆盖 Sudachi 插件的配置文件 sudachi.json。我们将更改默认配置中的 systemDict 和 userDict 字段。systemDict 字段我们将切换到一个最小的词典,用于实践。默认的系统词典是 system_core.dic。
{
"systemDict": "system_small.dic",
"userDict": [
"custom.dic"
],
"inputTextPlugin": [
{
"class": "com.worksap.nlp.sudachi.DefaultInputTextPlugin"
},
{
"class": "com.worksap.nlp.sudachi.ProlongedSoundMarkInputTextPlugin",
"prolongedSoundMarks": [
"ー",
"-",
"⁓",
"〜",
"〰"
],
"replacementSymbol": "ー"
}
],
"oovProviderPlugin": [
{
"class": "com.worksap.nlp.sudachi.MeCabOovProviderPlugin"
},
{
"class": "com.worksap.nlp.sudachi.SimpleOovProviderPlugin",
"oovPOS": [
"補助記号",
"一般",
"*",
"*",
"*",
"*"
],
"leftId": 5968,
"rightId": 5968,
"cost": 3857
}
],
"pathRewritePlugin": [
{
"class": "com.worksap.nlp.sudachi.JoinNumericPlugin",
"joinKanjiNumeric": true
},
{
"class": "com.worksap.nlp.sudachi.JoinKatakanaOovPlugin",
"oovPOS": [
"名詞",
"普通名詞",
"一般",
"*",
"*",
"*"
],
"minLength": 3
}
]
}
在使用systemDict中指定要使用的系统词典,在userDict中指定将来要构建的二进制词典文件。值得一提的是,userDict可以指定多个。在本例中,我们将在Docker环境中进行系统词典的添加和用户词典的构建。
Dockerfile的意思是:
现在让我们来创建一个Dockerfile,通过构建用户字典源文件并一次性启动Elasticsearch。
在elasticsearch目录中准备一个Dockerfile。内容如下所示。
FROM ibmjava:8-jre-alpine as dict_builder
## 辞書の種類の指定(small/core/full)
ARG sudachi_dict_type="small"
## ユーザー辞書ソースを持ってくる
COPY sudachi/custom_dict.txt /home
WORKDIR /home
# Sudachiプラグインのjarファイルを持ってくる (バイナリ辞書の作成のため)
RUN wget https://github.com/WorksApplications/elasticsearch-sudachi/releases/download/v7.4.0-1.3.1/analysis-sudachi-elasticsearch7.4-1.3.1.zip && \
unzip analysis-sudachi-elasticsearch7.4-1.3.1.zip && \
# 用意されているシステム辞書を持ってくる
wget https://object-storage.tyo2.conoha.io/v1/nc_2520839e1f9641b08211a5c85243124a/sudachi/sudachi-dictionary-20190718-${sudachi_dict_type}.zip && \
unzip sudachi-dictionary-20190718-${sudachi_dict_type}.zip && \
# バイナリ辞書の作成
java -Dfile.encoding=UTF-8 -cp /home/sudachi-0.3.0.jar com.worksap.nlp.sudachi.dictionary.UserDictionaryBuilder -o /home/custom.dic -s /home/sudachi-dictionary-20190718/system_${sudachi_dict_type}.dic /home/custom_dict.txt
FROM elasticsearch:7.4.0
ARG sudachi_dict_type="small"
# Sudachiプラグインの設定ファイル
COPY sudachi/sudachi.json /usr/share/elasticsearch/config/sudachi/
# 前ステージでダウンロードしたSudachiのシステム辞書
COPY --from=dict_builder /home/sudachi-dictionary-20190718/system_${sudachi_dict_type}.dic /usr/share/elasticsearch/config/sudachi/
# 前ステージで作ったユーザー辞書
COPY --from=dict_builder /home/custom.dic /usr/share/elasticsearch/config/sudachi/
# 前ステージでダウンロードしたプラグイン
COPY --from=dict_builder /home/analysis-sudachi-elasticsearch7.4-1.3.1.zip /usr/share/elasticsearch/
# Sudachiプラグインインストール
RUN elasticsearch-plugin install file:///usr/share/elasticsearch/analysis-sudachi-elasticsearch7.4-1.3.1.zip && \
rm /usr/share/elasticsearch/analysis-sudachi-elasticsearch7.4-1.3.1.zip
请确保Sudachi插件的版本与Elasticsearch的版本基本一致。我们采用多阶段构建的原因是不想在Elasticsearch的镜像中包含不必要的Java命令等无关物品。在dict_builder阶段主要进行从用户词典源文件构建二进制词典文件的操作。正如文档中所述,您可以使用以下命令从用户词典源文件创建二进制词典。
$ java -Dfile.encoding=UTF-8 -cp sudachi-XX.jar com.worksap.nlp.sudachi.dictionary.UserDictionaryBuilder -o output.dic -s system_core.dic [-d comment] input
输出.dic 是输出二进制字典文件的名称
system_core.dic 是 Sudachi 的系统字典
comment 被嵌入到二进制字典头部的注释
input.csv 是用户字典源文件的名称
当然,您可以将在本地构建的文件复制到Dockerfile中,但出于减少管理文件数量、避免在Git中管理庞大的字典二进制文件和简化Java环境设置的原因,我选择在Docker容器内完成整个过程。
确保动作正确
我将创建一个用于操作确认的docker-compose.yml。将Kibana包含在内是因为Kibana内置了一个称为DevTools的实用功能。
version: '3.6'
services:
elasticsearch:
build: ./elasticsearch
container_name: elasticsearch
environment:
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- discovery.type=single-node
- node.name=es01
ports:
- '9200:9200'
- '9300:9300'
kibana:
image: docker.elastic.co/kibana/kibana:7.4.0
links:
- elasticsearch
environment:
- ELASTICSEARCH_URL=http://elasticsearch:9200
ports:
- 5601:5601
立即启动。构建和启动只需下面一条命令。
$ docker-compose up --build
让我们立即通过浏览器访问 Kibana 的 URL(http://localhost:5601)。打开菜单,找到 DevTools,然后在控制台中输入以下内容。
PUT /sample-index
{
"settings": {
"number_of_shards": 1,
"analysis": {
"analyzer": {
"sudachi_analyzer": {
"type": "custom",
"tokenizer": "sudachi_tokenizer"
}
},
"tokenizer": {
"sudachi_tokenizer": {
"type": "sudachi_tokenizer",
"mode": "search",
"discard_punctuation": true,
"resources_path": "/usr/share/elasticsearch/config/sudachi/",
"settings_path": "/usr/share/elasticsearch/config/sudachi/sudachi.json"
}
}
}
},
"mappings": {
"properties": {
"text": {
"type": "text",
"analyzer": "sudachi_analyzer"
}
}
}
}
POST sample-index/_analyze
{
"analyzer": "sudachi_analyzer",
"text": "po3rin"
}
在Kibana上,会是这样的情况。

只需一种选择性地用中文进行释义:
tokenizer会根据在settings_path中指定的sudachi.json来理解要使用的词典。
只需使用DevTools,您就可以通过API对Elasticsearch进行操作。执行这些操作后,您可以确认以下响应。
{
"tokens" : [
{
"token" : "po3rin",
"start_offset" : 0,
"end_offset" : 6,
"type" : "word",
"position" : 0
}
]
}
在用户字典中注册的“po3rin”可以被识别为一个单词。
概括起来,就是这样。