自然言語処理の深遠

Deep Dive Into Natural Language Processing

Wikipediaのリンクを解析して同義語を抽出する

自然言語処理をする際、同義語を考慮したい時があります。 たとえば情報検索を例に挙げると、「car」と検索したときその同義語である「automobile」も検索にかかってくれると嬉しい場合があります。

同義語を獲得する方法は色々考えられますが、本記事では、Wikipedia のリンクを解析して、同義語を抽出します。 手法としてはかなり単純です。一言で言うと「リンク先が同じなら、それらのアンカーテキストは同義語である可能性が高い」という仮定に基づいて同義語を抽出します。

図にすると以下のようなイメージです。 f:id:Hironsan:20171031105429p:plain

では、日本語版 Wikipedia を用いて、同義語を抽出してみます。

手順

  • Wikipedia の本文を抽出
  • 本文からリンクを抽出
  • リンク情報をまとめ上げて同義語を抽出

データのダウンロード

ここでは Wikipedia のダンプデータをダウンロードします。 以下のファイルをダウンロードしてください。

Wikipedia の本文を抽出

Wikipedia からダンプデータをダウンロードしたら、wikiextractor で本文を抽出します。 ダウンロードした Wikipedia のダンプデータには本文以外の様々な情報が含まれています。 今回は、本文の情報だけを用いるので、wikiextractor を用いて、本文を抽出します。

まずは、wikiextractor を「git clone」して用意します。

$ git clone https://github.com/attardi/wikiextractor.git
$ cd wikiextractor

wikiextractor が用意できたら、Wikipedia のダンプデータから本文をリンク付で抽出します。 ただ、wikiextractor はデフォルトではリンクの情報を取り除いてしまいます。 今回はリンクの情報を含めたまま本文を抽出するために「--links」オプションを付けて実行します。

$ python WikiExtractor.py -o extracted --lists --links jawiki-20171001-pages-articles.xml.bz2

処理が終了すると、extractedディレクトリの中に抽出された本文のデータが格納されています。 形式は、HTMLのアンカーリンク(aタグ)と同じです。 ここでリンク先は、パーセントエンコーディングされた Wikipedia のページであり、アンカーテキストは「自然言語」です。

言語、特に<a href="%E8%87%AA%E7%84%B6%E8%A8%80%E8%AA%9E">自然言語</a>について述べる。

以降では、リンク先情報とアンカーテキストを用いて、同義語を獲得していきます。

本文からリンクを抽出

ここからは Python を用いて、抽出された本文からリンクを抽出し、同義語を獲得します。 今回、リンクを抽出するために、BeautifulSoup というライブラリを使用します。 BeautifulSoup を用いることで、XMLやHTMLファイルからリンクを簡単に抽出できます。

まずは、BeautifulSoupをインストールします。 pipを用いて以下のコマンドを実行することでインストールできます。

$ pip install beautifulsoup4

次に、必要なモジュールをインポートします。

import json
import glob
from urllib.parse import unquote
from collections import defaultdict, Counter

from bs4 import BeautifulSoup

必要なモジュールをインポートしたら、ファイルからリンクを出現頻度とともに抽出します。

d = defaultdict(Counter)
files = glob.glob('extracted/**/wiki*')
for file in files:
    print(file)
    with open(file) as f:
        doc = f.read()
    soup = BeautifulSoup(doc)
    for link in soup.find_all('a', href=True):
        if link.has_attr('href'):
            href = unquote(link['href'])
            text = link.text
            d[href][text] += 1

リンクの抽出が終わったら、その情報を保存します。

with open('result.json', 'w') as f:
    json.dump(d, f)

結果の確認

保存した情報を読み込んで、結果を確認してみましょう。 以下のような結果が得られました。

>>> import json
>>> with open('result.json') as f:
...     terms = json.load(f)
... 
>>> print(terms['Google'])
{'Google': 1400,
 'Google Inc.': 4,
 'Google Inc. により': 1,
 'Google Japan': 2,
 'Google Video': 1,
 'Google.ie': 1,
 'Googleの日本法人': 1,
 'Googleブログ検索': 1,
 'Googleモバイル': 1,
 'Google日本': 1,
 'Google社': 2,
 'グーグル': 38,
 'グーグル (Google)': 1,
 'グーグルサーチ': 1,
 'グーグル合同会社': 4,
 'グーグル株式会社(Google)': 1,
 'グーグル社': 1}

単語の隣に書かれた数字は、その出現頻度を表しています。 結果を見ると、出現頻度が高めの単語は使えなくもないかな?という感じですが、実用にはさらに手法を洗練する必要があるでしょう。

おわりに

本記事では、日本語版Wikipediaのリンク情報を用いて同義語を獲得する方法を紹介しました。 結果を見ると、実用で使うにはもう少し手法を洗練する必要があるかなという感じでした。 本記事が皆様のお役に立てば幸いです。

私のTwitterアカウントでも機械学習自然言語処理に関する情報をつぶやいています。

@Hironsan

この分野にご興味のある方のフォローをお待ちしています。