はじめての言語処理

〜Lucene編〜

A  g  e  n  d  a

  • きっかけ

  • Apache Luceneってなに?

  • 転置インデックスのしくみ

  • standerd analyzer と Japanese analyzer(Kuromoji)

  • インデックスの作成と検索

  • まとめというか感想

K  i  k  k  a  k  e.

  • 2014年12月17日のJJUG night セミナーでApache Luceneについて紹介しているセッションがあったため存在を知り興味を持ちました。

  • この時は正直あんまりよくわかってないまま、なんか面白そうというそれだけでお正月ごにょごにょしていました。

  • サンプルソースが読みやすかったので。

Apache Lucene Sammary

  • 全文検索ライブラリ

  • Java製

  • 転置インデックス方式採用

  • 形態素解析のシステムが内蔵されてます。すぐ使えます。

    • Standerd Analyzer

    • CJK Analyzer

    • Japanese Analyzer 

      • Kuromoji

      • Gosen

  • 数百万行の文書でも高速に検索が可能(らしい!)

I n v e r t e d   I n d e x

1.東京ディズニーランドは千葉にあります

2.ディズニーランドに遊びに行く時はパスポートが必要です

3.千葉県に遊びに行く時はパスポートは不要です

東京        1

ディズニーランド  1, 2

千葉        1, 3

遊び        2, 3

パスポート     2, 3

行く        2, 3

必要        2

不要        3

です        2, 3

ます        1      etc...

I n v e r t e d   I n d e x

  • 素早く文書の検索ができる
  • ので、大規模システムの検索に向いている

 

Good

Not good enough

  • 転置インデックス自体のメンテナンス
  • 文書追加・削除などに合わせたインデックスの更新

 

A  n  a  l  y  z  e  r

  • 英語:スペース区切り
  • 日本語:1-gram

Standerd Analyzer

Japanese Analyzer

  • 日本語形態素解析方式
    • lucene-gosen
    • Kuromoji

CJK Analyzer

  • 英語:スペース区切り
  • 日本語:1-gram

今回はKuromoji

を使ってみました

とりあえず

使ってみました

Comparison of the Analytical Technique

  • 辞書を用いてテキストから単語を抽出
  • MeCab / Kuromoji / Yahoo!デベロッパーの解析API etc.

形態素解析方式

Good 

辞書依存のため流行語や略語を解析させることができる

Not good enough

辞書のメンテナンスが必要

N-gram方式

  • 辞書を持たずに機械的にテキストから単語を区切って抽出

  • 1-gram / 2-gram / 3-gram .....

Good 

辞書レス。辞書メンテ不要

Not good enough

東京都問題に要注意。ノイズ増加の可能性の考慮が必要

M a k e   I n d e x

  1. 利用するアナライザーを決定する

  2. インデックスの書き込みモードを決定する

  3. インデックス生成スタート

//アナライザの選択
StandardAnalyzer analyzer = new StandardAnalyzer();

IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer);

//インデックス書き込み方法の決定
config.setOpenMode(IndexWriterConfig.OpenMode.CREATE);
IndexWriter writer = new IndexWriter(readyDirectory(), config);

//インデックス生成
try (FileReader fr = new FileReader(LuceneTestConfig.dataDir);
             BufferedReader br = new BufferedReader(fr)){

            //インデックスへデータを追加
            String text;
            while ((text = br.readLine()) != null) {
                writer.addDocument(getDocument(text));
            }
} catch { }


M a k e   I n d e x ~Open Mode~

Open Mode

インデックス作成時に、すでに出来上がっているデータをどのように取り扱うかを設定できる値。

CREATE

既存インデックスを全て削除

CREATE_OR_APPEND

既存インデックスが

ある場合:インデックスの追加

ない場合:新規インデックスの作成

APPEND

インデックスの追加

既存インデックスが存在しない場合はエラーになる

M a k e   I n d e x ~Custom Analyzer~

  1. Kuromoji トークナイザ設定

NORMAL

通常の単語分割

SEARCH

複合語を細かく分割。

例)「関西国際空港」→ 関西/国際/空港。「国際」や「空港」で「関西国際空港」をヒットさせることができる

EXTENDED

SEARCHモード+辞書にない語をuni-gramに分割。

例)「ディジカメ」→デ/ィ/ジ/カ/メ。

Tokenizer tokenizer = new JapaneseTokenizer(
                                          reader, null, false, JapaneseTokenizer.Mode.NORMAL);

M a k e   I n d e x ~Custom Analyzer~

    2. Kuromoji トークンフィルター設定

New JapaneseBaseFormFilter(torkenizer)

単語を基本形に変換する.    例)  「行け」→「行く」

New JapaneseReadingFormFilter(torkenizer)

単語の代わりに、単語の読みに変換する

例 )  「合格」→「ゴウカク」

TokenStream = new JapaneseBaseFormFilter(tokenizer);
TokenStreamComponents tscompornents = 
                      new TokenStreamComponents(tokenizer, tokenStream);

S e a r c h   I n d e x 

  1. 検索条件を設定する
  2. 検索スタート
try (Directory dir = readyDirectory();
             IndexReader reader = DirectoryReader.open(dir)){

   //検索条件の指定
            //Termはコンストラクタで検索対象とするフィールド名と、
           //キーワードを指定して生成を行う
            TermQuery termQuery = new TermQuery(
                                                          new Term(
                                                                   LuceneTestConfig.fieldName, searchWord));

            //検索。limitCount=返却件数
            IndexSearcher searcher = new IndexSearcher(reader);
            TopDocs topdocs = searcher.search(query, limitCount);
} catch { }

S e a r c h   I n d e x ~Plural words~

検索単語の解析と、検索モードの設定を行うと

複数単語による検索も実行できます。

//fieldName=インデックス作成時にインデックスを登録したフィールドの名前
Analyzer analyzer = new JapaneseAnalyzer();
            QueryParser parser = new QueryParser(fieldName, analyzer);

//パースのデフォルト検索モードはOR検索
//Query query = parser.parse(searchWord);
Query query =parser.createBooleanQuery(
             fieldName, searchWord, BooleanClause.Occur.MUST);

//検索。limitCount=返却件数
 IndexSearcher searcher = new IndexSearcher(reader);
 TopDocs topdocs = searcher.search(query, limitCount);

S e a r c h   I n d e x ~Search Mode~

Occur.MUST

AND検索

Occur.SHOULD

OR検索

Occur.MUST_NOT

NOT検索

S e a r c h   I n d e x ~Other tips~

  • 検索結果は以下値が返却されます。
    • インデックス登録時の行数
    • Score
  • 検索スコアのつけ方を変更することもできます
    • フィールドごとの asc desc (groupingモジュール)
    • インデックス検索結果のjoin(joinモジュール)
    • 地図情報を用いた検索結果 (sparialモジュール)

利用例)

  1. 日付ごとのフィールドにデータを格納しておき、最新記事を優先表示させる
  2. 複数の検索結果を使ってさらに分析しちゃう
  3. 検索したチェーン店の今いるところから一番近い店を探す

m a t o m e.

  • なにやら高速な全文解析ができそうです。

 

  • 用意されているモジュールを駆使すれば、柔軟な対応もできそうです。

 

  • 検索結果が読み込み時のファイル行数番号でしか返って来ない点は要注意

 

  • Lucene単体では商用環境での利用は難しそう。

  → Apache Solr やElasticsearch等を利用するといいらしいです!

    (すみません!こちらまで今回は手が回りませんでした!)

t h a n k s.

お勉強に使ったサイト
  • https://github.com/johtani/jjug-example/blob/master/src/main/java/info/johtani/jjug/lucene/sample/

  • https://speakerdeck.com/johtani/lucenetori-ben-yu-falsejian-suo

  • http://d.hatena.ne.jp/Kazuhira/20130602/1370163286

  • http://www.kakiro-web.com/lucene/index.html

  • http://thinkit.co.jp/free/article/0710/17/1/

fin.

HelloLucene

By maaya ishida

HelloLucene

  • 2,161