Wikipedia2vec使ってみた

# はじめに
本記事は、単語やエンティティのベクトル表現をWikipediaから学習できるツール「Wikipedia2Vec」の紹介と使用してみた感想を伝える記事。
※ 筆者はWikipedia2Vecの開発等に関わっておらず、Studio Ousiaの関係者でもないです。

# Wikipedia2vecについて

* 著名な学会やコンペにて多くの成果を残しており[^1]自然言語処理に定評がある日本の企業であるStudio Ousiaがオープンソースとして公開  
* Skip-gramを以下2つのモデルで拡張し学習
 * ![image.png](https://qiita-image-store.s3.amazonaws.com/0/264359/3a742a0e-7374-6b0f-a4a9-50ddf88d5b02.png)
 * **Link graph model**:  Wikipediaエンティティのリンクグラフに基づき、近傍のエンティティを推定することによりエンティティの関係性を学習する
 * **Anchor context model**: エンティティとそれに隣接する単語を指すリンクを使用して、エンティティの近傍ワードを予測することを学習する
* 12言語で利用可能・事前学習済みベクトル配布
 * 英語, アラビア語, 中国語, オランダ語, フランス語, ドイツ語, イタリア語, 日本語, ポーランド語, ポルトガル語, ロシア語, スペイン語

[^1]: http://www.ousia.jp/ja/research

# インストール ＆ 使い方
詳細は[ここ](https://wikipedia2vec.github.io/wikipedia2vec/install/)が詳しいが、以下のpipでインストールできる。

```
pip install wikipedia2vec
```

学習済みの埋め込みベクトルは以下のページよりダウンロードできる。
[Pretrained Embeddings - Wikipedia2Vec](https://wikipedia2vec.github.io/wikipedia2vec/pretrained/)

ダウンロードした埋め込みベクトルを読み込むことにより、以下のようにベクトルを取得することが可能である。([Basic Usage - Wikipedia2Vec](https://wikipedia2vec.github.io/wikipedia2vec/usage/)より)

```py
>>> from wikipedia2vec import Wikipedia2Vec

>>> wiki2vec = Wikipedia2Vec.load(MODEL_FILE)

>>> wiki2vec.get_word_vector('the')
memmap([ 0.01617998, -0.03325786, -0.01397999, -0.00150471,  0.03237337,
...
       -0.04226106, -0.19677088, -0.31087297,  0.1071524 , -0.09824426], dtype=float32)

>>> wiki2vec.get_entity_vector('Scarlett Johansson')
memmap([-0.19793572,  0.30861306,  0.29620451, -0.01193621,  0.18228433,
...
        0.04986198,  0.24383858, -0.01466644,  0.10835337, -0.0697331 ], dtype=float32)

>>> wiki2vec.most_similar(wiki2vec.get_word('yoda'), 5)
[(<Word yoda>, 1.0),
 (<Entity Yoda>, 0.84333622),
 (<Word darth>, 0.73328167),
 (<Word kenobi>, 0.7328127),
 (<Word jedi>, 0.7223742)]

>>> wiki2vec.most_similar(wiki2vec.get_entity('Scarlett Johansson'), 5)
[(<Entity Scarlett Johansson>, 1.0),
 (<Entity Natalie Portman>, 0.75090045),
 (<Entity Eva Mendes>, 0.73651594),
 (<Entity Emma Stone>, 0.72868186),
 (<Entity Cameron Diaz>, 0.72390842)]
```


# 使ってみた
日本語の自然言語からWikipediaエンティティを抽出し、抽出したエンティティベクトル間のコサイン類似度に基づく、類似度行列を作成しました。

### 実験結果
対象とした自然言語は、ハロー!プロジェクトのWikipedia記事より抜粋した以下の文。
>ハロー!プロジェクト（Hello! Project）は、アップフロントプロモーション（旧：アップフロントエージェンシー）をはじめとするアップフロントグループ系列の芸能事務所に所属している女性アイドルグループ・女性アイドルタレントの総称、またはメンバーのファンクラブの名称。略称はハロプロ（英語圏では"H!P"と略される）。2014年までは主につんく♂が総合プロデュースを手掛けていた。

得られた類似度行列の一部は以下のようになった。"ハロー!プロジェクト"と"Hello! Project"の類似度が1.0となっており、同じエンティティとして抽出されていることが確認できる。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/264359/d5220f77-465a-b145-b5d7-2c7a8eed0940.png)

また、得られた類似度行列に基づきグラフを作成し、Gephiを使って可視化した。パッと見た感じエンティティ間の関係性を適切に表現できているように見える。(色分けはグラフクラスタリングの結果です)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/264359/2fa91d07-21e5-807e-3d44-d4f511fb80db.png)



### コード

```py
import numpy as np
import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity

from janome.tokenizer import Tokenizer
t = Tokenizer(mmap=True)
from wikipedia2vec import Wikipedia2Vec
wiki2vec = Wikipedia2Vec.load('tools/jawiki_20180420_100d.pkl')

def get_wikipedia_entity( text ):
    entity = []
    for token in t.tokenize( text ):
        pos = token.part_of_speech.split(',')[0]
        if pos == '名詞':
            kEntity = wiki2vec.get_entity( token.base_form )
            if kEntity == None: continue
            entity.append( token.base_form )
    return entity

text = """
ハロー!プロジェクト（Hello! Project）は、
アップフロントプロモーション（旧：アップフロントエージェンシー）をはじめとする
アップフロントグループ系列の芸能事務所に所属している女性アイドルグループ・女性アイドルタレントの総称、またはメンバーのファンクラブの名称。
略称はハロプロ（英語圏では"H!P"と略される）。2014年までは主につんく♂が総合プロデュースを手掛けていた。
"""

entities = get_wikipedia_entity( text )
eNum = len(entities)
simmat = np.ndarray( ( eNum, eNum ) )
for i in range( eNum ):
    for j in range( eNum ):
        if i == j:
            sim = 1.0
        elif i > j:
            sim = simmat[j,i]
        else:
            v1 = wiki2vec.get_entity_vector(entities[i]).reshape(1, -1)
            v2 = wiki2vec.get_entity_vector(entities[j]).reshape(1, -1)
            sim = cosine_similarity( v1, v2)
        simmat[i,j] = sim
```

# 終わりに

本記事では、「Wikipedia2Vec」を紹介し、実際に使ってみました。
簡単な実験ではありましたが、エンティティ間の関係が適切に表現できることを確認しました。

感想としては、

- オープンソースはありがたい、楽しい
- インストールも簡単なのですぐに試せるのもGood
- 色んな応用できそう


# 参考

- Studio Osunaリリース記事: http://www.ousia.jp/ja/page/ja/2018/08/15/wikipedia2vec/
- 紹介ページ: https://wikipedia2vec.github.io/wikipedia2vec/
- Github: https://github.com/wikipedia2vec/wikipedia2vec