スキップしてメイン コンテンツに移動

ElasticSearchとKuromojiで日本語全文検索してみる

北海道開発オフなどでご一緒させて頂いた方はご存知だと思いますが、元々インフラエンジニアとしてネットワーク機器の設定やサーバの構築ばかりしていた僕がプログラムを書くようになったのは、「形態素解析してマルコフ連鎖するTwitter botを作りたい!」というのがきっかけであり、日本語の分かち書きや全文検索というのは僕にとって興味深くそして好きな技術の一つです。

そこで今回はElasticSearchとそのプラグインであるelasticsearch-analysis-kuromojiについて調べてみました。

ElasticSearchとは
Apache v2ライセンスで公開されているオープンソースソフトウェアの検索エンジンです。特徴として
  • RESTfulなAPIが使える
  • InputもOutputもJSON
  • スキーマフリーなので面倒臭い定義無しにデータを突っ込める
等があります。


Kuromojiとは
atilika社製の、Javaで書かれた日本語形態素解析ソフトウェアで、Apache v2ライセンスで公開されているオープンソースソフトウェアです。形態素解析としてはMeCabがメジャーで古くから使われていますが、Kuromojiは比較的新しいソフトウェアです。

ElasticSearchのセットアップ
環境はAmazon Linuxです。ElasticSearchの動作にはJavaが必要ですが、Amazon Linuxの場合初期導入されています。
$ java -version
java version "1.6.0_24"
OpenJDK Runtime Environment (IcedTea6 1.11.13) (amazon-65.1.11.13.56.amzn1-x86_64)
OpenJDK 64-Bit Server VM (build 20.0-b12, mixed mode)
ElasticSearchの公式サイトから最新バージョンをダウンロードします。なお僕は「サーバにインストールするソフトウェアは保守性の観点から可能な限りパッケージ管理するべき」というポリシーなのでRPMから入れます。
$ wget https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-0.90.5.noarch.rpm
$ sudo rpm -ivh ./elasticsearch-0.90.5.noarch.rpm
Preparing...                          ################################# [100%]
Updating / installing...
   1:elasticsearch-0.90.5-1           ################################# [100%]
Starting elasticsearch: [  OK  ]

ElasticSearchの動作確認
それでは簡単な動作確認をしてみます。まずはXPUTを使ってテストデータを登録します。
ここではIndexを「mytest」、Typeを「test」としました。Oracle用語で言うと、IndexがSchema、TypeがTableに当たるものになります。
$ curl -XPUT http://localhost:9200/mytest/test/1 -d '
> {
>   "title" : "memo",
>   "text"  : "hogehoge"
> }'
{"ok":true,"_index":"mytest","_type":"test","_id":"1","_version":1}
そしてXGETを使って検索してみます。
$ curl -XGET http://localhost:9200/mytest/test/_search -d '
> {
>   "query":
>   { "match":{"title":"memo"}}
> }'
{"took":85,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":1,"max_score":0.30685282,"hits":[{"_index":"mytest","_type":"test","_id":"1","_score":0.30685282, "_source" :
{
  "title" : "memo",
  "text"  : "hogehoge"
}}]}}
titleが「memo」であるデータが戻り値として返ってきました!

Kuromojiプラグインのセットアップ
ElasticSearchのpluginコマンドを使って、Kuromojiプラグインをインストールします。
$ sudo /usr/share/elasticsearch/bin/plugin  --install elasticsearch/elasticsearch-analysis-kuromoji/1.5.0
-> Installing elasticsearch/elasticsearch-analysis-kuromoji/1.5.0...
Trying http://download.elasticsearch.org/elasticsearch/elasticsearch-analysis-kuromoji/elasticsearch-analysis-kuromoji-1.5.0.zip...
Downloading ................................................................................................................................................................................................................................................................................DONE
Installed elasticsearch/elasticsearch-analysis-kuromoji/1.5.0 into /usr/share/elasticsearch/plugins/analysis-kuromoji
プラグインをインストールした後はElasticSearchの再起動が必要です。
$ sudo service elasticsearch restart
Stopping elasticsearch:                                    [  OK  ]
Starting elasticsearch:                                    [  OK  ]

Kuromojiプラグインの動作確認
それでは簡単な動作確認をしてみましょう。
kurotestというIndexで、kuromojiをアナライザと使うように設定します。
$ curl -XPUT 'http://localhost:9200/kurotest/' -d'
>  {
>      "index":{
>          "analysis":{
>              "tokenizer" : {
>                  "kuromoji" : {
>                     "type" : "kuromoji_tokenizer"
>                  }
>              },
>              "analyzer" : {
>                  "analyzer" : {
>                      "type" : "custom",
>                      "tokenizer" : "kuromoji"
>                  }
>              }
>          }
>      }
>  }'
{"ok":true,"acknowledged":true}
このkurotestにXPOSTで日本語文字列を投げると、分かち書きされて返ってきます!
$ curl -XPOST 'http://localhost:9200/kurotest/_analyze?analyzer=analyzer&petty' -d '梅酒は水'
{
  "tokens" : [ {
    "token" : "梅酒",
    "start_offset" : 0,
    "end_offset" : 2,
    "type" : "word",
    "position" : 1
  }, {
    "token" : "は",
    "start_offset" : 2,
    "end_offset" : 3,
    "type" : "word",
    "position" : 2
  }, {
    "token" : "水",
    "start_offset" : 3,
    "end_offset" : 4,
    "type" : "word",
    "position" : 3
  } ]
}
(余談ですが「梅酒は水」はWebbingStudioさんが北海道に広めた偉大な言葉です)

ElasticSearchとKuromojiプラグインで日本語全文検索
お待たせしました!ここからが本番です。
まずはElasticSearchの設定ファイルを修正し、kuromojiをデフォルトアナライザとして設定します。設定後はElasticSearchの再起動が必要です。
$ sudo vi /etc/elasticsearch/elasticsearch.yml
index.analysis.analyzer.default.type: custom
index.analysis.analyzer.default.tokenizer: kuromoji_tokenizer
$ sudo service elasticsearch restart
それでは二つの日本語文字列をテストデータとして登録してみます。
$ curl -XPUT http://localhost:9200/jptest/test/1 -d '
>   {
>     "title" : "メモ",
>     "text"  : "梅酒は水"
>   }'
{"ok":true,"_index":"jptest","_type":"test","_id":"1","_version":1}
$ curl -XPUT http://localhost:9200/jptest/test/2 -d '
>   {
>     "title" : "メモ2",
>     "text"  : "麦酒は命"
>   }'
{"ok":true,"_index":"jptest","_type":"test","_id":"2","_version":1}
梅酒」という文字列で検索してみます。
$ curl -XGET http://localhost:9200/jptest/test/_search -d '
>    {
>      "query":
>      { "match":{"text":"梅酒"}}
>    }'
{"took":3,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":1,"max_score":0.15342641,"hits":[{"_index":"jptest","_type":"test","_id":"1","_score":0.15342641, "_source" :
  {
    "title" : "メモ",
    "text"  : "梅酒は水"
  }}]}}
ちゃんと「梅酒は水」がヒットしました!

次に「梅酒」という文字列で検索してみます。
$ curl -XGET http://localhost:9200/jptest/test/_search -d '
>     {
       "query":>        { "match":{"text":"梅酒は"}}
>      }'
{"took":5,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":2,"max_score":0.2169777,"hits":[{"_index":"jptest","_type":"test","_id":"1","_score":0.2169777, "_source" :
  {
    "title" : "メモ",
    "text"  : "梅酒は水"
  }},{"_index":"jptest","_type":"test","_id":"2","_score":0.02250402, "_source" :
   {
     "title" : "メモ2",
     "text"  : "麦酒は命"
   }}]}}
「梅酒は水」と「麦酒は命」の2つがヒットしました。ここで注目したいのは赤字にした_scoreです。
「梅酒は水」は「梅酒は」のうち3文字がヒットするためスコアが高く、「麦酒は命」は「酒は」の2文字のみがヒットするためスコアが低くなっています。これで分かち書きによって日本語全文検索が行われていることがわかります。

感想
In/OutがJSONであることから、使い道は色々あるなぁと思います。例えばBlogの記事検索にも使えるし、fluentdと組み合わせてログ検索にも使るし。Kuromojiの分かち書きも試した分には精度良さそうなので、コレを使って何か作ってみたいです。



パーフェクトRuby (PERFECT SERIES 6)パーフェクトRuby (PERFECT SERIES 6)
Rubyサポーターズ,すがわら まさのり,寺田 玄太郎,三村 益隆,近藤 宇智朗,橋立 友宏,関口 亮一

技術評論社
売り上げランキング : 13917

Amazonで詳しく見る by AZlink

このブログの人気の投稿

努力できること自体が才能なので、努力しただけで褒められるべき

発明王トーマス・エジソンの名言としてよく知られる「天才とは1%のひらめきと99%の努力である」という言葉があります。実際の意図は「1%のひらめきがなければ、99%の努力は無駄になる」であったとも 言われています が、まぁどちらにせよ、ひらめきだけでは成功することはできず、そこには必ず努力も必要となります。 漫画「はじめの一歩」において主人公の師匠である鴨川会長は「努力した者が全て報われるとは限らん。しかし、成功した者は皆すべからく努力しておる」と言っていました。ここにも、成功において努力は必要不可欠であるとの強いメッセージがあります。 しかし、実際には誰もが努力できるわけではありません。 努力できること自体が才能 なのだと思います。 努力をしないことが問題だと言うつもりはありませんし、努力をしないという選択肢を選ぶことは個人の自由だと思います。必ずしも成功が万人の幸せなわけではないし、成功しなくても得られる生活によって満足する人だっています。 僕が言いたいのは「そもそも努力できない人がいる」という事実です。こういう方は選択の余地が無く、何かにおいて1位になったり、一流になったり、勝利したり、成功したりすることは難しくなります。それらには必ず努力が必要になるからであり、そして努力ができない、努力をする才能が無いからです。 これには実験の裏付けがあります。詳しくは10年前の WIREDの記事 に書かれていますが、ざっくり言うと、脳内の一部(左線条体と前頭前皮質腹内側部)におけるドーパミン作動性活性が高いと努力ができる、違う一部( 島皮質)の ドーパミン作動性活性が高いと努力できない、という実験結果があります。そもそも脳の作りや働きによって努力ができる人とできない人がいる、ということです。 繰り返しになりますが、僕は努力しない人が悪いとも思っていないし、責めるつもりもありません。僕が言いたいのは以下の2点です。 (1)努力できること自体が才能であり、その才能が無い人はそもそも努力ができないので 、努力できないことを責めてはいけない。 それは本人の特性であり個性だから。 (2)努力できること自体がすごいことなので、努力した結果が成功に結びつかなかったとしても、責めてはいけない。 努力しただけで褒められてよい。 人にはそれぞれ適材適所があるので、めちゃくちゃ努力する人は努力が

自走する組織に必要なのはルールではなくガイドライン

ということをいつも心がけている、という話です。 僕が組織のマネジメント職を20年ほどやらせてもらっている上で、いつも意識しているのは権限移譲とセルフマネジメントです。この辺の話は過去のブログにも書きました。 管理職のためのエンジニア組織構築マニュアル 管理職のための役職引退マニュアル 現場に口を出さないマネージャーの作り方 つまり「権限と裁量を同時に移譲し、責任感を持ってプロアクティブに仕事をしてもらいながらも、メンバーの良いところを更に引き出して高いパフォーマンスを出してもらう」ことこそが、マネジメント職のやるべきことだと思っています。 そのために僕がいつも権限移譲の際に伝えるのは、ルールではなくガイドラインです。ルールは規則や規定といった決まりごとなので「やること」「やってはいけないこと」が書かれたものです。ガイドラインは大まかな指針なので「方向性」「やったほうがいいこと」「やらないほうがいいこと」が書かれたものです。 ルールを提示した場合、そのとおりにすれば過去の実績からある程度の成功は見込めるものの、状況に応じた柔軟な判断が出来ませんし、メンバーの考えや意見が行動に反映されません。メンバーはルール通りの行動しか出来ず、結果としてルールを作成した人以上の成果は出せなくなってしまいます。 ガイドラインの場合、会社として望ましいと考える方向性だけが書かれているので、状況に応じた柔軟な判断も出来ますし、メンバーが考えるより良いやり方や行動を取り入れることが出来ます。ガイドラインを作成した人以上の良いアイデアがあればガイドラインをアップデートすることも出来ます。 これは権限移譲だけでなく、育成においても同様だと僕は考えます。1から10まで決まりきったカリキュラムをやらせることも時には(あるいは人によっては)必要だけれど、本当に価値のある育成は、メンバーに目指してほしい姿を伝え、現在とのギャップを一緒に認識し、そのギャップを埋めるための多種多様な方法を伝えて、その上で本人が取捨選択して自分自身で学習していく。企業や上長はそのサポートを行う。というのが、最も成長出来る育成方法だと思います。 学習する組織 ― システム思考で未来を創造する posted with AmaQuick at 2

「許可を得るな、謝罪せよ」が意図していること

 弊社ではセルフマネジメントとアウトプットファーストを行動指針として掲げていますが、セルフマネジメントを象徴する言葉としてよく使われるのが 「許可を得るな、謝罪せよ」 です。 細かい話は 以前ブログにした のでそちらを読んで頂くとして、この言葉が意味するのは「アクションするのにいちいち許可を得る必要はない。許可を取る時間が無駄。やっていいですかじゃなくてやりましたと言えばいい。その結果間違っていれば謝れば良いだけ」です。 何故この方針を取るのか、この方針によってどのような結果を期待しているのか、を改めて整理したいと思います。 アクションのスピードを上げたい これは上述した意味の通りで、何らかの施策や企画があるときに、上長の許可を取るために資料を作ったり、打ち合わせしたり、下調べをしたり、という時間が無駄だからです。 この考え方の前提として「小さな失敗を早くたくさんする」というのがあります。どんな施策も企画も、正解なんて誰にもわからないし、やり方次第で変わるものです。アイデアの時点であーだこーだ言うより、実際に手を動かしてやってみて、その結果から継続の判断を行うことで、リスクを小さく、コストも小さく、たくさんアクションすることが出来ます。 モチベーションを持って取り組んでもらいたい 何でもそうですけど、人に言われたことをそのままやるより、自分で考えたことを自分のやり方でやるほうが、面白いです。僕が仕事をする上で、または僕がピープルマネジメントする上で、一番重要視しているのは、面白いかどうか、です。 担当者がモチベーションのないままやって成功することなんて(ほとんど)ありません。その施策や企画の実施に一番モチベーションがあるのはそれを考えた人なので、その人に主導してもらうのが一番成功率が高いです。 主体性を持って取り組んでもらいたい モチベーションと同様に、担当者が主体性のないままやって成功することなんて(ほとんど)ありません。その施策/企画を自分ごととして捉え、だからこそ知恵を絞って、全力を発揮する、つまり主体性を持って取り組むことが、一番成功率が高いです。そしてもちろん、一番主体性を持てるのはそれを考えた人です。 なお、主体性と責任は違います。前述の通り「小さな失敗を早くたくさんする」ためには、失敗に対して責任を追求するのではなく、結果と知見を追求する、という文化が