本連載の内容
本連載で紹介予定の内容は次のとおりです。
Step1:Elasticsearchを用いた日本語検索システム
対話型の検索システムを作成するためのStep1として、Elasticsearchを用いた日本語検索システムの構築方法を紹介します。Elasticsearchの検索における仕組みから構築のポイントとなるElasticsearchの設定ファイル、登録用のテンプレート、辞書について解説します。
Step2:Elasticsearchへの大量データ登録の効率化
日本語検索システム作成の際に大量データ登録をする場合、シーケンシャルにデータを登録すると時間がかかってしまいます。そこで並列処理を用いて効率よくデータを登録する方法を紹介します。
Step3:検索システムを使いたくなるUI提供:Slack対応
検索システムを社内に導入しても、UIが適切でないと使ってもらえません。システムの本質は使ってもらうことにより価値が出ます。そこでSlackをインターフェースに挟むことにより、ユーザーが使いやすい検索システムの提供を試みます。Step3ではSlackのインターフェースを利用した簡単なBotの作成を行います。
Step4:SlackとElasticsearchを連携し、聞きやすく使いたくなる検索システムの提供
Step1、Step2の記事で実現した検索システムとStep3の記事で実現したSlackインターフェースをつなげることを試みます。インターフェースにはSlackを使用し、ElasticsearchのDockerコンテナとSlack対応BotのDockerコンテナを用意してSlack対応Bot、Elasticsearchのコンテナを変更できるようにして柔軟性に富んだ構成を目指します。
対象読者
- 現場の技術者で自社や自身のプロジェクトで検索システムを作成する必要のある方
- 環境差異の少ない検索システムを作成したい方
検証環境
この記事では、以下の環境でサンプルの動作を確認しています。
- Elasticsearch 2.3.0
- Vagrant 1.8.5
- Ubuntu Trusty 14.04 (LTS):Vagrant上での動作OS
- Docker 1.12
Elasticsearchについて
Elasticsearchとは、Elastic社のもとでOSSとして開発されている、大量にある文書の中から目的のワードを持つ文書を検索するための検索エンジンのことです。
OracleやPostgreSQLなどのRDBMSのデータベースとElasticsearchとの違いは、RDBMSは条件にマッチしたデータを正確に返すのと比べ、Elasticsearchは条件との関係性の高いデータを返すという点があります。
今回Elasticsearchを採用する理由は登録する文書は自然言語であり、自然言語は人によって記述に違いがあり、完全一致で検索する場合に目的のものを検索するのが難しいためです。
以下ではElasticsearchの検索機能にのみフォーカスして説明します。Elasticsearch 2.3.0時点での機能を想定しています。
Elasticsearchでは以下の2点の工夫をして検索機能の提供をしています。
- 文書を検索に適した形で保存。
- スコアリングによる検索結果の提示
文書を検索に適した形で保存
文章を直接保存せずに転置インデックスという手法を使用して文書を保存しています。転置インデックスとは、図1のように単語が登録されている文書の番号を保存しておくことで、検索の際に文書の中身を検索せずに該当文書を返す手法です。
処理の流れは下記です。
- 文書から単語を抽出
- 単語からストップワードと呼ばれる「は」や「で」などの機能語を外す
- 登録処理
登録されたTermと呼ばれる単語を例を挙げて説明すると、「Elastic」は「Document1」にしか存在しませんが、「OSS」は「Document1」と「Document2」に存在しています。
「Elastic」で検索すると「Document1」を返し「OSS」で検索すると「Document1」と「Document2」を返すようになっています。こうすることで、すべての文書を検索することなく高速で結果を返す仕組みを実現しています。
スコアリングによる検索結果の提示
先ほどの転置インデックスだけの検索では同一の文書の番号のものが複数出てきます。その結果に差異をつけるためにスコアリングという手法を使用しています。
スコアリングは下記の式で成り立ちます(デフォルト)。
score(q,d) = queryNorm(q) * coord(q,d) * SUM ( tf(t in d), idf(t)2, t.getBoost(), norm(t,d) ) (t in q)
「q」が検索クエリを表し「d」は対象の文書を表します。
検索クエリの例:
- 単語:「Docker」
- 複数単語:「Docker Elasticsearch」
- 文章:「DockerでElasticsearchの環境構築」
上から順に説明すると、次のとおりです。
- score(q,d)は最終的に出したいスコアです。
- queryNorm(q)は検索クエリのスコアが不当に高くならないように調整する係数です。
- coord(q,d)は文書と検索クエリの同時出現数です。
-
SUM(足し算)を行っている理由は検索クエリは単語だけではなく複数語や文章の場合もあります。そのため検索クエリを単語単位で区切って計算するためSUM(足し算)の処理を行っています。
- tf(t in d)は文書中に出てきた単語の頻度です。
- idf(t)は多くの文書に出てくる”私”などの単語は重要でないと判断するために使用する指標です。
- t.getBoost()"は各単語のBoost値を取得します。これは長い文の方が評価が高くなりやすいのでそれを避けるために設定されている値です。
- norm(t,d)は頻度は少ないが文書の特徴を表す重要な単語に対するスコアが小さくならないように調整する指標です。
これらの指標を用いて上記の結果を算出します。