自然言語処理
Elasticsearch
kuromoji
形態素解析
Sudachi

Elasticsearchのための新しい形態素解析器 「Sudachi」

tl;dr (要約)

  • Kuromojiに代わる新しい形態素解析器「Sudachi
  • なにが良いの?
    • 最新の辞書
    • 企業(ワークスアプリケーションズ)による継続的な更新
    • 複数の分割単位 → 検索用途での再現率と適合率の向上
    • プラグインによる拡張
    • 省メモリ
  • Elasticsearchで使いたい

注: この記事の執筆者はSudachiの開発に関わっています

さまざまな形態素解析器

形態素解析は、伝統的な自然言語処理(Natural Language Processing, NLP)において基盤となる技術です。そして世の中にはさまざまな形態素解析器が存在します。販売されているものもありますが、一般に公開されているものだけでもいくつか例をあげると、JUMANとRNNLMを利用したJUMAN++、チャンピオン・セグメンターことChaSenとその後続で現在最も著名であろうMeCab、点推定により部分アノテーションデータから学習できるKyTea、Elasticsearchにも公式採用されているkuromoji、そしてそれぞれピュアGo/JavaScript/Python実装で辞書同梱のkagome/kuromoji.js/Janomeなどなど。ソフトウェアではなくWeb APIという形で提供されるものもあります(Yahoo! JAPAN テキスト解析Web APIなど)。さまざまなものがあるので、複数の解析器をまとめて利用できるライブラリ(JapaneseTokenizers)もあったりします。

他にも、いわゆる形態素解析の定義からは少し外れますが、JavaScript実装のRakuten MA(単語の分かち書き+品詞付与)や、同じくJavaScript製のコンパクトな分かち書き器TinySegmenter、そしてより最近ではニューラル手法によるNLPのためのトークナイザSentencePieceなどもあります。また、これまでの形態素解析を抜本的に改善しようという高い理念のもとに開発されている日本語解析システム雪だるまも特筆すべきプロジェクトの一つでしょう。

形態素解析はほとんどの場合、辞書と、アノテーションされた学習データもしくは学習済のモデルが必要です1。言語資源としては、Unidic辞書/BCCWJコーパスやIPA辞書/コーパス、Juman辞書/京都コーパス、NAIST JDicなどが主なものです。これらは頻繁に更新されるわけではないので、Web上のテキストから新語を収集・収録しているNEologdを併用されている方も多いのではないでしょうか。

2018年3月に開催される言語処理学会第24回年次大会(NLP2018)では「形態素解析の今とこれから」と題したワークショップが予定されており、活発な議論が期待されます。また、MeCabやSentencePieceの作者でもある工藤拓さんによる一冊まるまる形態素解析という狂気じみた本も近日刊行予定とのことです。まさになぜか今、形態素解析がアツいのかもしれません。

... and again Yet Another 形態素解析器 「Sudachi」

そんな中、2017年につくられた新たな解析器が「Sudachi」です。既にさまざまなツールがある中、商業利用に耐えうる、より高品質で使い勝手の良い形態素解析器を標榜するものです。

開発は、株式会社ワークスアプリケーションズ下の機関であるワークス徳島人工知能NLP研究所を主として行われています。企業によるバックアップのもと、オープンソースソフトウェアとしてコードおよび言語資源を一般に公開しています。ライセンスはApache-2.0です。

Sudachiの特徴としてまず、最新の辞書が用意されているという点が挙げられます。既存の形態素解析器における辞書は、10年以上も前に更新が止まってしまっており、新語・固有表現も不足しています。一方でSudachiでは、UniDicとNEologdをもとに専門家が調整した高品質で大規模な資源を用意しており、今後10年は継続して更新していく予定です。

他には、複数の分割単位での出力という機能があります。一般に形態素解析器はひとつの単位でしか出力できませんが、SudachiではA,B,Cの3単位での出力が可能です。これはSudachiのシステム辞書に分割情報を付与することで実現しています。

例えば「医療品安全管理責任者」という入力のときには以下3種類の出力が可能です。

A: 医療 / 品 / 安全 / 管理 / 責任 / 者
B: 医療品 / 安全 / 管理 / 責任者
C: 医療品安全管理責任者

従来の形態素解析器では、AもしくはBのような単位でしか区切れませんでしたが、Sudachiではそれに加えてCのように固有表現も意味を持つ言語単位として判別することが可能です。

これにより例えば検索用途においては、短単位と長単位を併用することで、再現率と適合率を向上することができます(後述するElasticseachプラグインのsearchモードでは、A単位とC単位が併用されます)。

分割情報に加えて、Sudachiのシステム辞書では以下の表記正規化がカバーされています。

  • 送り違い
    • 打込む → 打ち込む
  • 字種
    • かつ丼 → カツ丼
  • 異体字
    • 附属 → 付属
  • 誤用
    • シュミレーション → シミュレーション
  • 縮約
    • ちゃあ → ては

このように高品質のシステム辞書ですが、利用者が自ら複数のユーザー辞書を用意して拡張することも可能です。

またこの他にも、プラグインによる機能拡張が可能です。これまで解析器の外で行っていた正規化や補正処理などのよくおこなわれる処理を同梱することができます。以下に述べるようなプラグインの種類があり、例に挙げたいくつかは既にシステム提供プラグインとして入っており、ほかも現在開発中です。

  • 入力テキスト: 異体字統制、長音記号正規化、など
  • 未知語: 1文字未知語ノードの作成、MeCab互換処理、など
  • 単語の連接: 品詞接続禁制、コスト値の書き換え、など
  • 出力解補正: 敬称や前後関係から人名部を推定、漢数詞や位取りの正規化、など

もちろん、自分でプラグインをつくって差し込むことも可能です。例えば、独自の未知語処理をしたり、対象データにあわせて品番や型式などの抽出をすることができます。

また、メモリマップ利用による複数JVMでの辞書共有により、省メモリを実現しています。実装の工夫については、JJUG CCC 2017 Fallでの発表をご覧ください。

MeCab、Kuromojiと比較すると以下のようになります。

Sudachi MeCab kuromoji
分割単位の併用 × △ ^1
文字正規化、表記正規化 × △ ^2
まとめ上げ、補正処理 × △ ^2
複数ユーザ辞書の利用 ×
省メモリ
解析精度
解析速度
  • ^1: n-best解による近似
  • ^2: Lucene フィルター併用

まだ公開して日は浅いですが、既にSyncThought社さんでの採用例もあります。

ここから、各種プラグインの作成、高速化や解析精度向上、同義語辞書との連携、分割情報や正規化表記の拡充、読み情報の整備、ユーザー辞書まわりの使い勝手向上、などなど、様々な面で形態素解析器として強化していきます。また、あわせてPython実装(その名もSudachiPy)も現在開発中です。簡単にインストールできて、手軽にデータ分析などに使えるよう整備していく予定です。

Sudachiについての解説は、以下の資料もあわせてご覧ください。

各種ソースコードはこちらから。

Sudachi for Elasticsearch

では実際に、SudachiをElasticsearchで動かしてみましょう!

今回の検証環境は以下のとおりです。

  • Elasticsearch 5.6.1
  • Ubuntu 17.10
  • Java 1.8.0_151

(環境やコードが整備されてきたため、以前書かれた SudachiをElasticsearchで使用する手順#1 - Qiita とは大きく違う手順に変わっています)

プラグインの入手とビルド

Elasticsearch用のSudachiプラグインは WorksApplications/elasticsearch-sudachi にあります。

このプラグインのpom.xmlではElasticsearchのバージョンを5.6.1としており、今回の検証でもバージョンをあわせています。5.6.xであれば、この部分を合わせて書き換えれば問題なく動作するでしょう。6系向けは現在対応中です、しばしお待ちください。

2018-01-09 追記: @matsuda_vla さんが、6系で動かした手順を書いてくださっています。
SudachiをElasticsearch6.1.1で使用する手順(非公式) - Qiita

まずソースコードを持ってきて、ビルドします。

$ git clone https://github.com/WorksApplications/elasticsearch-sudachi.git
$ cd elasticsearch-sudachi
$ mvn package

ビルドが無事完了すると、 elasticsearch-sudachi/target/releases/analysis-sudachi-1.0.0-SNAPSHOT.zip というような名前のファイルができます。

(2017-12-19追記: 自身でビルドする代わりに、Snapshot版リポジトリからダウンロードすることもできます。Kengo Todaさん、ご指摘ありがとうございました)

プラグインのインストール

次に、ビルドしたものをインストールします。これにはElasticsearchに用意されているelasticsearch-pluginというスクリプトを使います。installコマンドで、先ほどビルドしたファイルのパスを指定してください。

インストール後、listコマンドの結果にanalysis-sudachiが含まれていれば成功です。

$ sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install file:///path/to/builded/file/analysis-sudachi-1.0.0-SNAPSHOT.zip
$ /usr/share/elasticsearch/bin/elasticsearch-plugin list
analysis-sudachi

辞書の入手と設置

インストールに加えてもう一つ準備として、辞書ファイルを適切な場所に配置する必要があります。辞書ファイルは以下のURLからダウンロードできます。

Index of /repositories/snapshots/com/worksap/nlp/sudachi/0.1.1-SNAPSHOT

ここから最新版のdictionary_coreもしくはdictionary_fullを入手してください。

  • dictionary_core: 専門家による語彙の追加や調整がなされた高品質な辞書。UniDicをベースに、会社名、住所、駅名、人名などが追加されている。語彙数150万程度
  • dictionary_full: dictionary_coreへ語彙をさらに追加した上位集合。語彙数300万弱程度
$ wget https://oss.sonatype.org/content/repositories/snapshots/com/worksap/nlp/sudachi/0.1.1-SNAPSHOT/sudachi-0.1.1-20171208.130044-8-dictionary-core.tar.bz2
$ tar xf sudachi-0.1.1-20171208.130044-8-dictionary-core.tar.bz2
$ ls
system_core.dic

入手した辞書ファイルは、基本的に$ES_HOME/sudachi/下に置きます。後述の設定ファイルを書き換えることで、別の場所に設置したり、ファイルの名前を変えたりすることができます。

$ sudo mkdir /etc/elasticsearch/sudachi
$ sudo mv system_core.dic /etc/elasticsearch/sudachi

プラグインの確認

最後に、Elasticsearchを再起動して、プラグインが読み込まれているか確認してみましょう。pluginsanalysis-sudachiが入っていれば成功です。

$ sudo /etc/init.d/elasticsearch restart
$ curl -X GET 'http://localhost:9200/_nodes/plugins?pretty'

...
  "plugins" : [
    {
      "name" : "analysis-sudachi",
      "version" : "1.0.0-SNAPSHOT",
      "description" : "The Japanese (Sudachi) Analysis plugin integrates Lucene Sudachi analysis module into elasticsearch.",
      "classname" : "com.worksap.nlp.elasticsearch.sudachi.plugin.AnalysisSudachiPlugin",
      "has_native_controller" : false
    }
  ]
...

インデックスのための設定

これでElasticsearchでSudachiを使う準備が整いました。次にインデックスを作成して、実際に解析結果を見てみましょう。

まず、インデックスを作成するための設定を用意します。ここではanalysis_sudachi_settings.jsonという名前のファイルに以下の内容を書き込みました。

  • tokenizer
    • sudachi_tokenizer を指定
  • mode
    • normal, search, extended から指定。デフォルトはsearch
    • normal: 通常の分割
    • search: A単位とC単位
    • extended: searchに加えて未知語のユニグラム
  • discard_punctuation
    • 句読点を除くか。デフォルトはtrue
  • settings_path: Sudachiの設定ファイルのパス
    • 指定がなければ、Sudachiに同梱されている sudachi.json を利用
    • この設定ファイルで辞書ファイル名などを指定
    • デフォルトでは辞書ファイル名(systemDict)はsystem_core.dicとなっている
    • system_full.dicを利用したり、異なる名前の辞書ファイルを利用する際には、上のsudachi.jsonを書き換えたような設定ファイルを用意し、このsettings_pathで指定
    • 相対パスの場合、後述のresources_pathからの相対
    • 基本的に$ES_HOME以下でないとNG
  • resources_path: 辞書ファイルのあるディレクトリのパス
    • 指定がなければ、$ES_HOME/sudachi/
    • 基本的に$ES_HOME以下でないとNG
analysis_sudachi_settings.json
{
  "settings": {
    "index": {
      "analysis": {
        "tokenizer": {
          "sudachi_tokenizer": {
            "type": "sudachi_tokenizer",
            "mode": "search",
        "discard_punctuation": true,
            "resources_path": "/etc/elasticsearch/sudachi",
            "settings_path": "/etc/elasticsearch/sudachi/sudachi.json"
          }
        },
        "analyzer": {
          "sudachi_analyzer": {
            "filter": [
            ],
            "tokenizer": "sudachi_tokenizer",
            "type": "custom"
          }
        }
      }
    }
  }
}

インデックスの作成

この設定を書いたanalysis_sudachi_settings.jsonを使って、インデックスを作成します。ここではsudachi_testという名前で作ってみましょう。

$ curl -X PUT 'http://localhost:9200/sudachi_test/' -d @analysis_sudachi_settings.json
{"acknowledged":true,"shards_acknowledged":true,"index":"sudachi_test"}

作成したインデックスが正しく設定されているか見てみましょう。

$ curl -X GET 'http://localhost:9200/sudachi_test/?pretty'
{
  "sudachi_test" : {
    "aliases" : { },
    "mappings" : { },
    "settings" : {
      "index" : {
        "number_of_shards" : "5",
        "provided_name" : "sudachi_test",
        "creation_date" : "1512992783788",
        "analysis" : {
          "analyzer" : {
            "sudachi_analyzer" : {
              "type" : "custom",
              "tokenizer" : "sudachi_tokenizer"
            }
          },
          "tokenizer" : {
            "sudachi_tokenizer" : {
              "mode" : "search",
              "resources_path" : "/etc/elasticsearch",
              "type" : "sudachi_tokenizer",
              "discard_punctuation" : "true"
            }
          }
        },
        "number_of_replicas" : "1",
        "uuid" : "DBqj21t7STS8ZE2gM32MXw",
        "version" : {
          "created" : "5060199"
        }
      }
    }
  }
}

Sudachiプラグインを使った解析

これでSudachiプラグインの導入が終わりました。実際にどのように解析されるか、見てみましょう。

以下の例では「東京都知事選挙」というテキストを渡していますが、複数の分割単位("東京都知事選挙"という塊と、"東京/都/知事/選挙"という各部分)が出力されているのが確認できます。これはsearchモードとsystem_full辞書をつかった結果です。分割モードを変えたり、辞書を変更したり後述のフィルターを加えることで、この結果も違うものになります。

$ curl -X GET 'http://localhost:9200/sudachi_test/_analyze?analyzer=sudachi_analyzer&pretty' -d "東京都知事選挙"
{
  "tokens" : [
    {
      "token" : "東京都知事選挙",
      "start_offset" : 0,
      "end_offset" : 7,
      "type" : "word",
      "position" : 0,
      "positionLength" : 4
    },
    {
      "token" : "東京",
      "start_offset" : 0,
      "end_offset" : 2,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "都",
      "start_offset" : 2,
      "end_offset" : 3,
      "type" : "word",
      "position" : 1
    },
    {
      "token" : "知事",
      "start_offset" : 3,
      "end_offset" : 5,
      "type" : "word",
      "position" : 2
    },
    {
      "token" : "選挙",
      "start_offset" : 5,
      "end_offset" : 7,
      "type" : "word",
      "position" : 3
    }
  ]
}

様々なフィルター

上のようにナマのSudachiを使うだけでなく、用途に応じて様々なフィルターを適用することもできます。以下のフィルターが用意されています。

  • sudachi_part_of_speech
    • 設定に書かれた品詞のトークンを除外
    • デフォルト設定は stoptags.txt
  • sudachi_ja_stop
    • 日本語のストップワードを除外
    • stopwords.txt でストップワードの一覧を確認可能
  • sudachi_baseform
    • 動詞と形容詞の終止形化
    • 例: 飲み → 飲む
  • sudachi_readingform
    • カタカナ、もしくはローマ字の読みに変換
    • 例1: 寿司 → スシ
    • 例2: 寿司 → sushi

これらのフィルターは基本的に、Kuromojiのものとほぼ互換性があるように作成しています。多くの場合、kuromojiという部分をsudachiと書き換えるだけで動作するでしょう。より詳しい設定方法や利用例は、READMEの「Corresponding Filter」セクションをご覧ください。

おわりに

今回は形態素解析器「Sudachi」と、そのElasticsearchでの利用について紹介しました。

整備された短単位と長単位、語義的・文字的な表記統制、プラグインによる柔軟な拡張など、Sudachiのさまざまな特徴は、Elasticsearchでの用途において大きなメリットを提供することができると思っています。

これからさらにSudachiを強化していきますが、まだまだ知見や利用例、ドキュメントが不足しています。利用してみて、分からない点やご意見ご注文、怪しい挙動などありましたら、ぜひGitHubのIssueを立てたりしていただけると嬉しいです!

ちなみに明日の Elastic{ON} Tokyo 2017 にも徳島から上京して参加します。徳島県のゆるキャラ「すだちくん」のシャツを着て銘菓「すだちパイ」を持っていきますので、見かけたらぜひお声がけください!

sudachi.jpg

明日12/14のElastic stack Advent Calendar 2017は、ZooBontaさんによるLogstash関連の記事です!


  1. 教師なしの形態素解析や単語分割についてはここでは言及しませんが、教師ありとは別に非常にエキサイティングな分野だと思っています!興味のある方は(持橋+ 2009)やその解説, 実装(内海+ 2015)やその解説(Fujii+ 2017)、そしてデータ工学ロボティクス研究会での持橋先生による資料や、TokyoCLでの若林先生の資料などをご参照ください