WikipediaのデータからElasticsearch用類義語辞書をつくる

Elasticsearchには類義語によるクエリ拡張機能があります。これを適用すると `まどマギ` と検索したときに `まどかマギカ` と書かれた文書もヒットするようになります。
(LuceneやSolrにもありますがここではElasticsearchの話だけします)

この類義語辞書は、人手で作ること (e.g., [FRILの商品検索をnGramから形態素解析にした話 - mosowave](http://mosowave.hatenablog.com/entry/2015/12/07/000000)) もできますが、今回はなるべく手間をかけたくないのでWikipediaのリダイレクトデータから自動で類義語辞書を作る方法を紹介します。

(自動といってもノイズも含まれてるので実用的に使うにはある程度人手でフィルタリングする必要があります。それでも一から人手で作るよりは手間が少ないと思います)

([ElasticsearchではWordNetでの類義語検索に対応しているよう](https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-synonym-tokenfilter.html#_wordnet_synonyms)ですが、これを書いてる2015年12月時点では[日本語WordNetのサイト](http://nlpwww.nict.go.jp/wn-ja/)にアクセスできなくてあきらめた...)

## 類義語検索について
### 類義語辞書のフォーマット
Elasticsearchの[Synonym Token Filter](https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-synonym-tokenfilter.html)のページによると2種類の書き方があります。

1.`=>`を書くと、左のワードで検索したときに右のワードがヒットするというようなマッピング

```
i-pod, i pod => ipod,
sea biscuit, sea biscit => seabiscuit
```

2.カンマ区切りだとそれぞれのワードにヒットする (たとえば `ipod` で検索すると `ipod, i-pod, i pod` がヒットして、`i-pod` や `i pod` で検索したときも同様にヒットする)

```
ipod, i-pod, i pod
foozball , foosball
universe , cosmos
```

ここでは2のカンマ区切りのフォーマットでマッピングしていきます。

### トークナイズ
類義語検索はn-gramでトークナイズするとうまくいきません。たとえば、1-3gramでインデックスを作った場合だと4文字以上の類義語はヒットしません (吉本で検索してもよしもとがヒットしない). また、過剰に類義語拡張してしまうという問題もあります (「日本」で検索したときに「日本」の類義語 `大日本国,日本国,日本國,にほん,ニッポン,にっぽん,和国,ニホン` だけでなく「本」の類義語 `図書,BOOK,本,著書` も検索してしまう)

なので [Elasticsearch Analysis Kuromoji Neologd](https://github.com/codelibs/elasticsearch-analysis-kuromoji-neologd) などの形態素解析器でトークナイズするとよいです。

## Wikipediaのリダイレクト機能
> リダイレクト（転送）とは、ある記事へリンクしたときに、別のページに転送する機能のことです。また、そのようなページをリダイレクトページ（転送ページ）と呼びます。
from [Wikipedia:リダイレクト](https://ja.wikipedia.org/wiki/Wikipedia:%E3%83%AA%E3%83%80%E3%82%A4%E3%83%AC%E3%82%AF%E3%83%88)

たとえば、[https://ja.wikipedia.org/wiki/マミさん](https://ja.wikipedia.org/wiki/%E3%83%9E%E3%83%9F%E3%81%95%E3%82%93) にアクセスすると [https://ja.wikipedia.org/wiki/巴マミ](https://ja.wikipedia.org/wiki/%E5%B7%B4%E3%83%9E%E3%83%9F) のページにジャンプします。

このリダイレクト機能は以下のようなときに使われます。

- 表記ゆれ
- 同義語（類義語）
- 改名・改称
- 短縮名・略称
- 通称・俗称
- ペンネーム・芸名・別名
- 字（あざな）・雅号
- 愛称・あだ名
- 正式名・本名
- よくある間違った表記
- 既発売品のマイナーチェンジ版
- 曖昧さ回避ページへのリンク

このうちのどれも転送前と転送後で同じものを指していることがほとんどです。今回はこの特徴に着目してこのリダイレクト一覧から類義語辞書を作ります。

転送前と転送後のマッピングを得るために [Wikimedia Downloads](https://dumps.wikimedia.org/) からダウンロードします。

- ページタイトルのデータ
 - https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-page.sql.gz
- リダイレクトのデータ
 - https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-redirect.sql.gz

## 類義語辞書生成コード

https://github.com/ikegami-yukino/misc/blob/master/data/wikipedia_synonym.py

```bash

$ curl -o wikipedia_synonym.py https://raw.githubusercontent.com/ikegami-yukino/misc/master/data/wikipedia_synonym.py
$ python wikipedia_synonym.py
```

実行すると `wikipedia_synonym.txt` という類義語辞書ができます。

```
$ wc -l wikipedia_synonym.txt
  296172 wikipedia_synonym.txt
```
2015年12月時点では296172行のファイルができました。

## 動作確認

https://gist.github.com/ikegami-yukino/bf83daa4dc4f0e3d4787

実行すると、`モッツァレラチーズを食べました` と検索して `モッツァレッラチーズを食べました` がヒットしました！大成功！✌(՞ةڼ◔)✌️

## おわりに

WikipediaのリダイレクトデータからElasticsearchの類義語辞書を生成する方法を紹介しました。

検索にはいろいろな用途がありますが、類義語によるクエリ拡張をするとtrue positive (所望のものがヒットする) が増える一方で false positive (所望でないものがヒットする) も増えてしまう傾向があります。なので、「間違いがあってもいいからとにかくたくさんヒットするとうれしい」っていう用途には向いていて、「間違ったものがヒットすると致命的」という用途には向いてないと思います。

false positive多い！👿って思ったら[タイトルをフィルタリングするところ](https://github.com/ikegami-yukino/misc/blob/master/data/wikipedia_synonym.py#L51)の条件を厳しくするといいかも。(いずれにしろ最終的にはある程度人手で不要なデータをフィルタリングする必要があると思います)

それと類義語辞書の語彙を充実させるとその分だけメモリ使用量も増えるのでその点も要注意です。