Spark

はじめての word2vec with Spark

More than 3 years have passed since last update.

この記事は, Spark, SQL on Hadoop etc. Advent Calendar 2014 - Qiita の 12 月6日のための記事です.

今回の記事では,最近話題になっている word2vec の Apache Spark 実装を EC2 上で動かす方法について解説します.

word2vec とは?

自然言語処理に新風を巻き起こしたWord2Vecとは何か - 日経BigData の説明を拝借すると,word2vec とはつぎのような説明になります

Word2Vecは、その名前の表す通り、単語をベクトル化して表現するする定量化手法である。例えば日本人が日常的に使う語彙数は数万から数十万といわれるが、Word2Vecでは各単語を200次元くらいの空間内におけるベクトルとして表現する。
それぞれの単語を200個の要素の組み合わせとして表現するため、このような手法は「分散表現」とも呼ばれている。単語からベクトル表現を作り出す研究は以前にもあったが、それらとの違いは、そのベクトルがただの数学的な存在として以上に、複雑なコンセプトを表現していることにある。実例を見てみるとその可能性が実感できるだろう。

Apache Spark に実装されている word2vec は,Daabricks 社の Xiangrui Meng と Pivotal 社の Liquan Pei によって実装されました.
Google 本家の word2vec - Tool for computing continuous distributed representations of words. - Google Project Hosting に対して提供されている Public API はまだまだ足りなくて,対象の単語と Cosine 距離が近い単語を返すなどができません.
しかし大規模データに対して割と高速に実行できるので,お試しに実行してみるとなかなかおもしろいです.

環境構築

Amazon EC2 上で Apache Spark のクラスタを構築するためのスクリプトは,Spark のソースコードに含まれています.
こちらのスクリプトで1コマンドで簡単に構築することができます.

今回は,つぎのような構成でクラスタを立ち上げます.

  • Spark version: 1.1.1
  • Master Instance Type: r3.large
  • Slave Instance Type: r3.8xlarge
  • # Slave Instances: 5
    • Total CPU Cores: 160
    • Total Memory: 1195.0 GB
# clone spark
git clone https://github.com/apache/spark.git 
cd spark

# launch a spark cluster
_REGION='ap-northeast1'
_ZONE='ap-northeast-1b'
_VERSION='1.1.1'
_MASTER_INSTANCE_TYPE='r3.large'
_SLAVE_INSTANCE_TYPE='r3.8xlarge'
_SLAVES=5
_PRICE=1.0
_CLUSTER_NAME="spark-cluster-v${_VERSION}-${_SLAVE_INSTANCE_TYPE}x${_SLAVES}"
./ec2/spark-ec2 -k "YOUR_KEY_NAME" -i "YOUR_SSH_KEY" -s $_SLAVES --master-instance-type="$_MASTER_INSTANCE_TYPE" --instance-type="$_SLAVE_INSTANCE_TYPE" --region="$_REGION" --zone="$_ZONE" --spot-price=$_PRICE --spark-version="${_VERSION}" --hadoop-major-version=2 launch "$_CLUSTER_NAME"

実際に word2vec を動かしてみる.

データの準備

今回利用したデータは, Wikipedia の英語版のデータ になります.
Wikipedia のデータを扱いやすくするための記事は,Wikipediaデータをxml2sqlを利用しMySQLにぶっこむ - Miningoo が参考になります.
抽出した元のテキストデータは,約 38 GB になりました.

word2vec のモデル構築

Wikipedia のダンプデータから Wikimarkup のテキスト部分を抜き取り,Word2Vec でモデルを構築するためのコードはつぎのようになります.

// Imports the dependent libraries
import org.apache.spark.mllib.feature.Word2Vec
import org.apache.spark.mllib.linalg.Vectors
import org.apache.spark.mllib.linalg.Vector
import org.apache.spark.rdd.RDD
import org.apache.spark.SparkContext
import org.apache.spark.SparkContext._
import org.apache.spark.SparkFiles

// Loads the input file.
val path = "s3n://xxxxxxx/wikipedia/en/text-en.txt"
val source: RDD[String] = sc.textFile(path)

// Formats the input file.
val lines = source.map(line => line.replaceAll("""^[0-9]*\s*""", ""))
val input = lines.map(content => content.split(" ").map(_.replaceAll("""(\n|\[|\s|\]|'|\|\|)""", "")).toSeq).cache
input.count

// Trains a Word2Vec model.
val model = new Word2Vec().setVectorSize(10).setNumPartitions(10).fit(input)

構築したモデルにおける関連単語の抽出

構築したモデルを用いて類似語を抽出するための処理はつぎになります.

model.findSynonyms("America", 5).foreach(println)
model.findSynonyms("Japan", 5).foreach(println)

Apache Spark で Word2Vec を使う上での課題

Apache Spark の Mllib で実装されている Word2Vec は,現状大規模データに対して適応するには2つの点で難しいです.
1つは,精度をあげようと思うと MapReduce アルゴリズムの Reduce 部分を少なく設定しなければならないので,大規模なデータに適応すると多くの場合足りなくなります.
また同様の理由で,処理速度が落ちてしまうのも欠点です.
2つめは,構築されたモデルは各単語とそのベクトルを通常の Scala の Map[String, Array[Float]] として表現されています.
そのためあまりにもたくさんの単語があるときは,Map として保持できなくなってしまいます.