ブログ

PySparkで日本語形態素解析

こんにちは、ソフトウェアエンジニアの冨田です。
Apache Sparkでの分散処理をPythonで書いていて、日本語の形態素解析をしたくなったことはないでしょうか。私はあります。
そこで、今回はPythonで利用可能な形態素解析エンジンで有名なMeCabを使ってWikipediaの日本語記事全文を形態素解析してみたいと思います。

Sparkでの形態素解析

まずはローカルのSpark環境でMeCabを動かすことを目指します。Spark上でPythonのライブラリを使う場合には、全てのノードに同じライブラリがインストールされている必要があります。まずはローカルで試すだけなので、手元の環境にMeCabとPythonバインディングをインストールします。
まずは簡単に日本語の文字列を、単語のリストに変換するための関数を定義してしまいます。

class JapaneseTokenizer(object):
    def __init__(self) -> None:
        self.mecab = MeCab.Tagger("-Ochasen")
        self.mecab.parseToNode('')
    def split(self, text: str) -> List[str]:
        node = self.mecab.parseToNode(text)
        words = []
        while node:
            if node.surface:
                words.append(node.surface)
            node = node.next
        return words
def tokenize(text: str) -> List[str]:
    tokenizer = JapaneseTokenizer()
    return tokenizer.split(text)

日本語の文字列を読み込んだRDDに対してこの関数を適用すると、Spark上でもMeCabが実行されていることが確認できます。

spark = SparkSession.builder.appName('Tokenize').getOrCreate()
sc = spark.sparkContext
rdd = sc.parallelize(['株式会社フライウィール', '日本語形態素解析'])
rdd = rdd.map(lambda x: tokenize(x))
print(rdd.collect())
# [['株式会社', 'フライウィール'], ['日本語', '形態素', '解析']]

次はクラスタ上でMeCabを走らせることを目指します。

Google Dataproc で使うには

実際に複数マシンのクラスタ上で実行するには、MeCabを全ノードにインストールしておかねばならず、少し手間がかかります。特に、何十台のマシンでクラスタを組む場合は、大仕事です。
クラウドで提供されているマネージドのSpark環境を使用するのであれば、その手間も少し減らせる可能性があります。例えばGoogleが提供しているCloud Dataprocでは、カスタムイメージを作成し使用することがサポートされています。あらかじめMeCabやその他必要なライブラリをインストールしたイメージを用意さえしておけば、あとはCloud Dataprocがそのイメージを元にしてクラスタを構成してくれます。
DataprocはデフォルトではPython2で実行されますが、カスタムイメージを作成するとPython3系でも動かすことができます。カスタムイメージ作成時にPython3系をインストールしたのち、PYSPARK_PYTHONという環境変数を追加してあげると、Sparkはそこで指定されたPythonを参照するようになります。
また、Cloud Dataprocを使う場合はCloud Storage内のデータも簡単に参照できるので、大規模なデータもCloud Storageにさえ保存されていれば簡単に形態素解析できます。

Wikipediaの名詞の出現回数を数えてみる

実際に私が用意したカスタムイメージでCloud Dataprocのクラスタを作成し、Wikipediaの全日本語記事の本文を形態素解析し名詞の出現回数を数えてみたので、手順を簡単に紹介します。
まず、カスタムイメージの作成手順に沿ってMeCabなどの必要なライブラリを入れたイメージを用意します。
次に解析対象となるデータを用意します。今回はWikipediaの全日本語記事ということで、Wikipediaが提供しているダンプデータをダウンロードし、各記事の本文をそれぞれ1つのテキストファイルとしてCloud Storageに保存しました。11月30日のダンプで試した結果、ファイル数にして112万、容量にすると5.5GBほどのテキストファイルが用意できました。
先ほど紹介したPythonのコードに少し手を加えます。単語分割をした時に単語のリストでなく名詞のリストを返すような変更と、その結果を使って全記事中の名詞の数をカウントする簡単なコードを追加しました。
用意したスクリプトをn1-standard-4のワーカーノード5台を使って処理したところ、53分で処理が終わりました。同じ処理をローカル・シングルスレッドで行うスクリプを書いて実行したところ17時間36分も掛かった処理なので、大幅に処理時間が削減できていることが実感できます。
では、頻出名詞TOP 10をみてみましょう。

46592348	*
6398173	年
2751903	月
2087497	日
2031072	こと
1049867	的
1042564	ため
934749	人
840016	者
818483	日本

なんの面白みもないですが、名詞と誤認識されてしまった * を除くと、日付に使われる年、月、日などが上位という納得感ある結果となりました。
今回は単語の出現頻度を数えただけですが、SparkのMLlibを活用するとTF-IDFによる重み付けや、Word2Vecによる文書の特徴ベクトルなども簡単に計算することができます。もし大規模なコーパスに対してこれらの値を計算したい場合は、Sparkを活用することも検討してみてはいかがでしょうか。