今日も窓辺でプログラム

外資系企業勤めのエンジニアが勉強した内容をまとめておくブログ

Wikipediaの日本語記事を全行を、分かち書きしてforループで回す

はじめに

機械学習の勉強をするにあたって、日本語Wikipedia全文を学習データにできるとよさそう、ということでデータのダウンロードから、分かち書きされた文章をPythonのfor文で回す段階にもっていく方法をまとめました。

環境

  • Ubuntu 16.04.3
  • Python 3.5.2

コード

今回使用したコードはこちらにおいてあります。
github.com

Wikipedia全記事のダウンロード

Wikipediaの全記事は、定期的にダンプして保存されているようです。この記事を書いている時点では、以下の2017/11/03のダンプが最新のものでした。
jawiki dump progress on 20171103

jawiki-20171103-pages-articles.xml.bz2 という 2.4 GB ほどの大きさのファイルが全記事のデータなので、このファイルをダウンロードします。

Wikipedia Extractor

ダウンロードしてきたダンプデータには、Wiki記法の記号なども含まれており、そのまま自然言語処理をするには向きません。
そのような記号等を取り除き、きれいな日本語にしてくれるのが、Wikipedia Extractorというツールです。

私は下記GitHubからWikiExtractor.pyをダウンロードし、
GitHub - attardi/wikiextractor: A tool for extracting plain text from Wikipedia dumps

このようなコマンドを走らせて記事本文を抽出しました。

python WikiExtractor.py  jawiki-20171103-pages-articles.xml.bz2 -o extracted

するとこんな形で、<doc> ... </doc> という要素が1つのファイルにいくつか並んだXMLが得られました。

~/corpus/wikipedia/extracted/AA$ head wiki_00
<doc id="5" url="https://ja.wikipedia.org/wiki?curid=5" title="アンパサンド">
アンパサンド

アンパサンド (, &) とは「…と…」を意味する記号である。ラテン語の の合字で、Trebuchet MSフォントでは、と表示され "et" の合字であることが容易にわかる。ampersa、すなわち "and per se and"、その意味は"and [the symbol which] by itself [is] and" である。

その使用は1世紀に遡ることができ (1)、5世紀中葉 (2,3) から現代 (4-6) に至るまでの変遷がわかる。
Z に続くラテン文字アルファベットの27字目とされた時期もある。

行ごとにfor文を回す

<doc> ... </doc>ごとにイテレーションを回すDocumentIterator、<doc> ... </doc>中の1行ごとにイテレーションを回すSentenceIteratorを書きました。
テキストが大きくなっても全ファイルをメモリに載せないようにしたのですが、今回は2.4GB程度なのでメモリに載せてしまう実装でもよかったかもしれません。

これらを使ってWikipediaの全行をループで回せる wiki_sentences() なるジェネレータも用意しました。

前準備をした後であれば、

for text in wiki_sentences():
    print(text)

とかしてあげると、全行を順番に処理できます。

センテンスと言っていますが、実際にはセンテンスではなく、Wikipediaで改行なしで書かれている文字列のまとまりを順次取得できるイテレーターです。文書や段落を1文ごとに分割してあげる処理を入れてあげればよいのですが、面倒なので今回はこのままでいきます。今後の処理に支障が出るようであれば、後々実装するかもしれません。

分かち書きして一つのファイルに保存

後々の処理で使う際に分かち書きした1文が1行に入っているファイルがあると都合がよいので用意します。
文章の単語への分割にはみんな大好きMeCabを使用しました。

インストールの際には、この辺りのサイトを参考にしました:

MeCabのインストール
Python3で形態素解析エンジンMeCabを使えるようにする(2016年3月版) - Qiita

mecab-ipadic-NEologd辞書のインストール
mecab-ipadic-neologd/README.ja.md at master · neologd/mecab-ipadic-neologd · GitHub


MeCabの結果を処理する簡単なクラス Sentence を用意して、

class Sentence(object):
    def __init__(self, root):
        self.root = root
        self.surfaces = []
        self.features = []

        if self.root:
            node = root
            while node:
                self.surfaces.append(node.surface)
                self.features.append(node.feature)
                node = node.next

    def all_words(self):
        for surface, feature in zip(self.surfaces, self.features):
            yield surface, feature

    def word_count(self):
        return len(self.surfaces)

    def to_wakati(self):
        return ' '.join([w for w in self.surfaces if w])

この Sentence.to_wakati を呼び出すことで MeCab の結果から分かち書きの文章を用意しました。

Wikipediaの全文に対して分かち書きし、その結果をファイルに保存するクラスがWakatiCorpusクラスになります。

このクラスは結局面倒くさくなって全文章をメモリに載せているので、実行時には注意してください。。

実行してみる

ではさっそく実行してみます。

wakati = WakatiCorpus()
wakati.run()
wakati.save('wakati_corpus.txt')

生成されたファイルを一部確認してみると、1行ごとに分かち書きされた文章(実際には段落)が入っているファイルが用意できていることが確認できます。

~/corpus/wikipedia$ head wakati_corpus.txt
悔返
悔返 ( くい かえし ) と は 、 中世 日本 において 、 和 与 ・ 寄進 など の 財産 処分 を 行っ て 所有 権 の 移動  が 行わ れ た 後 に 元 の 所有 者 あるいは その 子孫 ら が その 行為 を 否定 し て 取り戻す 行為 。
平安 時代 の 公家 法 において は いかなる 場合 でも 悔返 は 認め られ て い なかっ た と する の が 通説 と さ れ て いる が 、 教 令 違反 や 不孝 など 律令 法 における 廃嫡 に 相応 する よう な 罪 の 事実 が 子孫 に あれ ば 、 「 後 状 有効 説 」 によって 悔返 は 認め られ た と する 異説 ( 長又 高 夫 ら ) も ある 。 鎌倉 時代 に 入る と 、 公家 法 において も 子孫 に対する 悔返 が 場合 によって は 認め られる よう に なっ て い た が 、 その 場合  において も 親 の 保護 下 に ない 既婚 女子 へ の 譲与 分 の 悔返 の 禁止 など の 厳しい 制約 が 設け られ 、 特に 本人 もしくは 和 与 を 受け た 子孫 が 第三者 に 譲っ た 場合 ( 他人 和 与 ) に は いかなる 場合 でも 悔返 は  認め られ なかっ た 。
...



おわりに

これで欲しかったデータは準備できました。次回以降は、これを使ってword2vecの学習などを行っていけたらと考えています。