用 Elasticsearch + Sudachi + Docker 创建用户词典进行实操

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

Sudachi 是什么

undefined

Sudachi是一种日本语形态素解析器,由株式会社ワークスアプリケーションズ下属的ワークス徳島人工知能NLP研究所开发。它具有支持多个分割单位等特点。有关文档请点击此处:https://github.com/WorksApplications/Sudachi/#sudachi-日本语readme

这次实操的最终设计

最终目标是追求以下的构成。

.
├── docker-compose.yml
└── elasticsearch
    ├── Dockerfile
    └── sudachi
        ├── README.md
        ├── custom_dict.txt
        └── sudachi.json
undefined

自定义词典文本

首先,让我们创建一个用户自定义词典源文件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上,会是这样的情况。

スクリーンショット 2019-10-18 18.46.20.png

只需一种选择性地用中文进行释义:
tokenizer会根据在settings_path中指定的sudachi.json来理解要使用的词典。
只需使用DevTools,您就可以通过API对Elasticsearch进行操作。执行这些操作后,您可以确认以下响应。

{
  "tokens" : [
    {
      "token" : "po3rin",
      "start_offset" : 0,
      "end_offset" : 6,
      "type" : "word",
      "position" : 0
    }
  ]
}

在用户字典中注册的“po3rin”可以被识别为一个单词。

概括起来,就是这样。

bannerAds