Y's note

Web技術・プロダクトマネジメント・そして経営について

本ブログの更新を停止しており、今後は下記Noteに記載していきます。
https://note.com/yutakikuchi/

Mahoutを使ったNaiveBayesによる機械学習

入門 ソーシャルデータ ―データマイニング、分析、可視化のテクニック

入門 ソーシャルデータ ―データマイニング、分析、可視化のテクニック

BigDataでの機械学習

膨大なデータに対して機械学習を行いたい時にlocalの端末一台では処理の時間が掛かりすぎてしまいます。学習、モデル作成、予測のそれぞれの処理を高速で行うための一つのSolutionがHadoop上で機械学習をしてしまうことだと思います。Hadoop上で機械学習をするための便利なライブラリとしてJAVAベースのMahoutがあります。この記事ではMahoutによるNaiveBayes分類学習を中心としたMahoutデータの生成と使い方について紹介します。
Machine Learning With Hadoop - Yuta.Kikuchiの日記 はてなブックマーク - Machine Learning With Hadoop - Yuta.Kikuchiの日記

NLTKによる分かち書き

NLTK Install

Python自然言語処理ライブラリのnltkを利用して分かち書きを行います。nltkのセットアップは非常に簡単で以下のコマンドを実行するだけです。Installing NLTK ― NLTK 2.0 documentation はてなブックマーク - Installing NLTK ― NLTK 2.0 documentation

$ python -V
Python 2.6.6
$ wget "http://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c11-py2.6.egg#md5=bfa92100bd772d5a213eedd356d64086"
$ sudo sh setuptools-0.6c11-py2.6.egg --prefix=/usr/
$ sudo easy_install pip
$ sudo pip install -U numpy
$ sudo pip install -U pyyaml nltk
Mecab Install

形態素解析器で有名なMecabPythonから利用できるようにします。Mecab本体、Mecab-ipadic、mecab-pythonの3つをinstallします。mecab-pythonのinstallの時に"mecab-config: コマンドが見つかりません"のように怒られたらsetup.pyのmecab-configをmecabをinstallした時のlocalディレクトリを指定するように修正するとinstallできます。

// mecab本体
$ wget http://mecab.googlecode.com/files/mecab-0.99.tar.gz
$ tar -xzf mecab-0.99.tar.gz
$ cd mecab-0.99
$ ./configure --with-charset=utf8
$ make && sudo make install

// mecab-ipadic
$ wget http://sourceforge.net/projects/mecab/files/mecab-ipadic/2.7.0-20070801/mecab-ipadic-2.7.0-20070801.tar.gz/download
$ tar -xzf mecab-ipadic-2.7.0-20070801.tar.gz
$ cd mecab-ipadic-2.7.0-20070801
$ ./configure --with-charset=utf8
$ make && sudo make install

// mecab-python
$ wget http://mecab.googlecode.com/files/mecab-python-0.993.tar.gz
$ tar -xzf mecab-python-0.993.tar.gz
$ cd mecab-python-0.993
$ python setup.py build
$ sudo python setup.py install
libmecab.so.2の読み込み

上の設定が完了しただけではまだPythonからMeCabが読み出せません。libmecab.so.2のエラーが出てしまうのでそれを回避するために設定ファイルに/usr/local/libを追記してldconfigの再読み込みを行います。これでmecab-pythonが起動できる準備が整いました。

$ sudo vim /etc/ld.so.conf.d/lib.conf
/usr/local/lib  //追記

// 再読み込み
$ sudo ldconfig

// mecab-pythonの起動
$ python
Python 2.6.6 (r266:84292, Sep 11 2012, 08:34:23) 
[GCC 4.4.6 20120305 (Red Hat 4.4.6-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import MeCab
データの抽出と分かち書き

Apache Mahout 機械学習Libraryを使って「魔法少女まどか☆マギカ」の台詞をテキストマイニングしてみた - Yuta.Kikuchiの日記 はてなブックマーク - Apache Mahout 機械学習Libraryを使って「魔法少女まどか☆マギカ」の台詞をテキストマイニングしてみた - Yuta.Kikuchiの日記 ここでも書いたようにまどマギの台詞を抽出してテキストファイルに落とします。更にテキストファイルに落としたデータに対して分かち書きを行います。分かち書きをした結果をディレクトリ名とファイル名をディレクトリ人物としたパスに保存します。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os,sys,re,urllib,urllib2

if( os.path.exists( "./data" ) != True ) :
   os.mkdir( "./data" )

urls = { 'http://www22.atwiki.jp/madoka-magica/pages/131.html' : 'madoka.txt', 
         'http://www22.atwiki.jp/madoka-magica/pages/57.html'  : 'homura.txt',
         'http://www22.atwiki.jp/madoka-magica/pages/123.html' : 'sayaka.txt',
         'http://www22.atwiki.jp/madoka-magica/pages/130.html' : 'mami.txt',
         'http://www22.atwiki.jp/madoka-magica/pages/132.html' : 'kyoko.txt',
         'http://www22.atwiki.jp/madoka-magica/pages/56.html'  : 'kyube.txt'
        }
opener = urllib2.build_opener()
ua = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/534.51.22 (KHTML, like Gecko) Version/5.1.1 Safari/    534.51.22'
referer = 'http://www22.atwiki.jp/madoka-magica/'
opener.addheaders = [( 'User-Agent', ua ),( 'Referer', referer )]
for k,v in urls.iteritems():
    f = open( './data/' + v , 'w' )
    content = opener.open( k ).read()
    if re.compile( r'^「(.*?)」$', re.M ).search( content ) is not None: 
        lines = re.compile( r'^「(.*?)」$', re.M ).findall( content )
        for line in lines:
            f.write( line + "\n" )
f.close()
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys,os
reload(sys)
sys.setdefaultencoding('utf-8')

import MeCab
mecab = MeCab.Tagger('-Ochasen')
names = [ 'homura', 'kyoko', 'kyube', 'madoka', 'mami', 'sayaka' ]

for name in names :
    if( os.path.exists( name ) != True ) :
        os.mkdir( name )
    file = './data/' + name + '.txt'
    f = open( name + '/' + name + '.txt', 'w' )
    for line in open( file ):
        line = line.strip().rstrip()
        p = mecab.parseToNode( line )
        phrases = p.next
        while phrases:
            try:
                k = p.surface
                f.write( k + " " )
                p = p.next
            except AttributeError:
                break 
        f.write( "\n" )
    f.close()
$ vi madoka/madoka.txt
 あっ … ?  
 ひどい …   
 そんな … あんまり だ よ 、 こんな の って ない よ  
 本当 な の ?  
 私 なんか でも 、 本当に 何 か できる の ? こんな 結末 を 変え られる の ?  
 夢 オチ … ?  
 おはよう 、 パパ  
 ママ は ?  
 はぁ い  
 おっき ろ 〜!  

Mahout用のデータ生成

HDFSへデータのPUT

Hadoopでデータを扱えるように上で生成した形態素ファイルをHDFSに全てputします。putするデータは形態素ファイルをsplitして1行ずつのファイルに落とします。アップする前に適当なディレクトリを一つ作っておくと良いと思います。/usr/lib/hadoop-0.20/bin/hadoop fsとタイピングするのが面倒なのでalias hdfs="/usr/lib/hadoop-0.20/bin/hadoop fs"としています。

$ cd homura
$ split -l 1 homura.txt
$ hdfs -mkdir madmagi
$ hdfs -put homura madmagi/
$ hdfs -put kyoko madmagi/
$ hdfs -put kyube madmagi/
$ hdfs -put madka madmagi/
$ hdfs -put madoka madmagi/
$ hdfs -put mami madmagi/
$ hdfs -put sayaka madmagi/
$ hdfs -lsr /user/yuta/madmagi
(略)
-rw-r--r--   1 yuta supergroup        128 2012-11-12 23:57 /user/yuta/madmagi/sayaka/xld
-rw-r--r--   1 yuta supergroup        339 2012-11-12 23:57 /user/yuta/madmagi/sayaka/xle
-rw-r--r--   1 yuta supergroup         58 2012-11-12 23:57 /user/yuta/madmagi/sayaka/xlf
-rw-r--r--   1 yuta supergroup         40 2012-11-12 23:57 /user/yuta/madmagi/sayaka/xlg
-rw-r--r--   1 yuta supergroup        228 2012-11-12 23:57 /user/yuta/madmagi/sayaka/xlh
-rw-r--r--   1 yuta supergroup         98 2012-11-12 23:57 /user/yuta/madmagi/sayaka/xli
-rw-r--r--   1 yuta supergroup        216 2012-11-12 23:57 /user/yuta/madmagi/sayaka/xlj
-rw-r--r--   1 yuta supergroup         10 2012-11-12 23:57 /user/yuta/madmagi/sayaka/xlk
SequenceFile生成

Hadoopでデータを扱えるようにHDFSにputしたデータをSequenceFileに変換します。変換にはmahoutコマンドのseqdirectoryを利用します。文字コードUTF-8を指定します。生成したファイルを確認するためにはseqdumperを利用します。seqdumperにて確認するとそれぞれのラベルが付けられたファイルの先頭指定文字を表示してくれます。

$ mahout seqdirectory  --input madmagi --output madmagi_seq -c UTF-8
$ mahout seqdumper --input madmagi_seq --substring 10
Key: /sayaka/xlc: Value:  あの さあ 、 キ
Key: /sayaka/xld: Value:  まさか あんた 、
Key: /sayaka/xle: Value:  はあ 、 どっち 
Key: /sayaka/xlf: Value:  … 何 か 、 手
Key: /sayaka/xlg: Value:  … うん 。 これ
Key: /sayaka/xlh: Value:  そう だ よ 。 
Key: /sayaka/xli: Value:  それ を 思い出せ
Key: /sayaka/xlj: Value:  まあ 、 そりゃ 
Key: /sayaka/xlk: Value:  うん  
VectorData生成

SeqenceFileが生成された後にMahoutでのinputファイルとして利用できるようにVectorデータに変換する必要があります。変換にはseq2sparseを利用します。オプションとして最大のDF値とNGramのサイズを指定しています。生成されたVectorDataを確認するためには先ほどと同様にseqdumperを利用します。形態素のデータがIDベースのベクトルデータに変換されている事が確認できます。

$ mahout seq2sparse --input madmagi_seq --output madmagi_vector --maxDFPercent 40 --maxNGramSize 8 --sequentialAccessVector --namedVector
$ mahout seqdumper --input madmagi_vector/tf-vectors/part-r-00000 --substring 100

Key: /sayaka/xla: Value: /sayaka/xla:{5:2.0,7:2.0,11:2.0,26:1.0,27:1.0,29:1.0,31:2.0,32:2.0,34:3.0,37:1.0,41:1.0,42:1.0,44:1.
Key: /sayaka/xlb: Value: /sayaka/xlb:{3:1.0,11:1.0,23:1.0,31:1.0,34:1.0,37:1.0,47:1.0,118:1.0,841:1.0,842:1.0,844:1.0,845:1.0
Key: /sayaka/xlc: Value: /sayaka/xlc:{3:3.0,5:2.0,9:1.0,12:2.0,13:1.0,17:1.0,21:2.0,27:1.0,29:1.0,31:1.0,35:1.0,37:1.0,38:2.0
Key: /sayaka/xld: Value: /sayaka/xld:{3:2.0,5:1.0,7:2.0,11:2.0,21:1.0,23:1.0,24:1.0,31:1.0,38:1.0,39:2.0,41:2.0,44:1.0,45:1.0
Key: /sayaka/xle: Value: /sayaka/xle:{3:1.0,5:2.0,7:2.0,11:4.0,13:2.0,17:1.0,19:1.0,21:1.0,23:3.0,29:1.0,31:1.0,32:4.0,33:2.0
Key: /sayaka/xlf: Value: /sayaka/xlf:{11:2.0,17:1.0,21:1.0,27:1.0,31:1.0,33:1.0,34:1.0,44:1.0,63:1.0,170:1.0,410:1.0,752:1.0,
Key: /sayaka/xlg: Value: /sayaka/xlg:{5:2.0,7:1.0,19:1.0,38:1.0,67:1.0,71:1.0,75:1.0,978:1.0,992:1.0,1274:1.0,1634:1.0,1635:1
Key: /sayaka/xlh: Value: /sayaka/xlh:{3:1.0,5:1.0,7:2.0,11:2.0,12:1.0,13:1.0,17:1.0,23:1.0,29:1.0,31:4.0,32:4.0,34:4.0,37:1.0
Key: /sayaka/xli: Value: /sayaka/xli:{5:2.0,7:1.0,17:1.0,27:1.0,29:1.0,31:1.0,32:2.0,38:1.0,41:1.0,45:1.0,62:2.0,67:1.0,71:1.
Key: /sayaka/xlj: Value: /sayaka/xlj:{3:1.0,5:7.0,15:2.0,17:1.0,21:1.0,23:2.0,24:1.0,27:1.0,29:1.0,31:1.0,32:1.0,33:1.0,34:3.
Key: /sayaka/xlk: Value: /sayaka/xlk:{7:1.0,75:1.0,1634:1.0}

$ mahout seqdumper --input madmagi_vector/tfidf-vectors/part-r-00000 --substring 100
Key: /homura/homura.txt: Value: /homura/homura.txt:{4:3.386294364929199,76:4.1972246170043945,95:1.6931471824645996,98:1.69314718246
Key: /kyoko/kyoko.txt: Value: /kyoko/kyoko.txt:{0:1.6931471824645996,28:2.932616949081421,78:11.104812622070312,79:11.301372528076
Key: /kyube/kyube.txt: Value: /kyube/kyube.txt:{0:1.6931471824645996,28:2.932616949081421,80:1.6931471824645996,81:2.9326169490814
Key: /madoka/madoka.txt: Value: /madoka/madoka.txt:{4:2.3944716453552246,81:1.6931471824645996,95:2.3944716453552246,97:2.3944716453
Key: /mami/mami.txt: Value: /mami/mami.txt:{88:2.967885971069336,97:2.3944716453552246,105:1.6931471824645996,107:3.634903192520
Key: /sayaka/sayaka.txt: Value: /sayaka/sayaka.txt:{57:3.6349031925201416,90:2.967885971069336,91:2.967885971069336,93:4.69263982772

NaiveBayesによる分類学習

NaiveBayes

ようやくNaiveBayesの話ができるようになりました。NaiveBayesは教師あり学習の一つで単純ベイズ分類器とも呼ばれ独立過程とベイズの定理に寄って算出される確率ベースの分類器です。
単純ベイズ分類器 - Wikipedia はてなブックマーク - 単純ベイズ分類器 - Wikipedia
MahoutでNaiveBayesを利用するにはtrainnbとtestnbを使用します。まずはtrainnbにて予測Modelを作成、testnbにてModelからの予測を行い評価します。今回のinputはまどマギの台詞ですが、ラベル済みのデータを学習させて未ラベルの評価データから台詞の人物を予測する事を目的としています。

予測Model作成

inputはtfidfのデータを利用します。生成したデータのoutput先指定とtrainComplementaryというオプションで精度を高めることが出来るようです。生成されたModelはバイナリファイルのようでseqdumperでも中身を確認する事が出来ませんでした。labelindexは登場人物に対して正解ラベルとなる整数値を割り当てています。

$ mahout trainnb --input madmagi_vector/tfidf-vectors/part-r-00000 --output madmagi_model --extractLabels --labelIndex madmagi_labelindex --trainComplementary

$ mahout seqdumper --input /user/yuta/madmagi_model
Exception in thread "main" java.io.IOException: hdfs://localhost:8020/user/yuta/madmagi_model/naiveBayesModel.bin not a SequenceFile

$ mahout seqdumper --input /user/yuta/madmagi_labelindex
Key: homura: Value: 0
Key: kyoko: Value: 1
Key: kyube: Value: 2
Key: madoka: Value: 3
Key: mami: Value: 4
Key: sayaka: Value: 5
予測の実行

生成したModelを利用してデータの予測を行います。ここでのinputはModel生成に利用した学習データと全く同一のものを利用します。学習データと評価データが一致しているので正解率が高くなるのは当然の事で、ここでは1880行中1686行のデータを当てていて、正解率は89.6809%となっています。

$ mahout testnb --input madmagi_vector/tfidf-vectors/part-r-00000 --output madmagi_test --model madmagi_model --labelIndex madmagi_labelindex
Standard NB Results: =======================================================
Summary
-------------------------------------------------------
Correctly Classified Instances          :       1686	   89.6809%
Incorrectly Classified Instances        :        194	   10.3191%
Total Classified Instances              :       1880

=======================================================
Confusion Matrix
-------------------------------------------------------
a    	b    	c    	d    	e    	f    	<--Classified as
238  	8    	7    	8    	5    	8    	 |  274   	a     = homura
3    	209  	0    	8    	1    	4    	 |  225   	b     = kyoko
5    	3    	312  	5    	3    	3    	 |  331   	c     = kyube
21   	13   	2    	498  	28   	24   	 |  586   	d     = madoka
2    	0    	2    	1    	158  	4    	 |  167   	e     = mami
5    	2    	1    	11   	7    	271  	 |  297   	f     = sayaka
学習データと評価データの分離

予測Modelの正確な精度を確認するために学習データと評価データを分けてtestnbしてみます。データを分けるコマンドとしてsplitがあります。下では50%ずつデータを分割しています。分割したデータに対して予測Modelを作成、評価を行ってみると正解度は50.4634%となりました。以前SVMでも似たような事を試していてその時の精度がイマイチだったので、今回の結果ももっと低いと予測していたんですが、まぁそれなりの結果が出たと言えると思います。
Support Vector Machinesを用いた「魔法少女まどか☆マギカ」人物予測モデル - Yuta.Kikuchiの日記 はてなブックマーク - Support Vector Machinesを用いた「魔法少女まどか☆マギカ」人物予測モデル - Yuta.Kikuchiの日記

$ mahout split --input madmagi_vector/tfidf-vectors --trainingOutput madmagi_train --testOutput madmagi_output --randomSelectionPct 50 --method sequential --sequenceFiles --overwrite

$ mahout trainnb --input /user/yuta/madmagi_train/part-r-00000 --output madmagi_model --extractLabels --labelIndex madmagi_labelindex --trainComplementary

$ mahout testnb --input /user/yuta/madmagi_output/part-r-00000 --output madmagi_test_output --model madmagi_model --labelIndex madmagi_labelindex

Standard NB Results: =======================================================
Summary
-------------------------------------------------------
Correctly Classified Instances          :        490	   50.4634%
Incorrectly Classified Instances        :        481	   49.5366%
Total Classified Instances              :        971

=======================================================
Confusion Matrix
-------------------------------------------------------
a    	b    	c    	d    	e    	f    	<--Classified as
46   	20   	15   	13   	29   	14   	 |  137   	a     = homura
8    	61   	5    	9    	12   	10   	 |  105   	b     = kyoko
12   	9    	110  	12   	25   	11   	 |  179   	c     = kyube
36   	25   	4    	165  	31   	45   	 |  306   	d     = madoka
15   	1    	4    	9    	46   	12   	 |  87    	e     = mami
22   	11   	3    	39   	20   	62   	 |  157   	f     = sayaka