Mahoutを使ったNaiveBayesによる機械学習
入門 ソーシャルデータ ―データマイニング、分析、可視化のテクニック
- 作者: Matthew A. Russell,奥野陽(監訳),佐藤敏紀(監訳),瀬戸口光宏(監訳),原川浩一(監訳),水野貴明(監訳),長尾高弘
- 出版社/メーカー: オライリージャパン
- 発売日: 2011/11/26
- メディア: 大型本
- 購入: 18人 クリック: 779回
- この商品を含むブログ (42件) を見る
BigDataでの機械学習
膨大なデータに対して機械学習を行いたい時にlocalの端末一台では処理の時間が掛かりすぎてしまいます。学習、モデル作成、予測のそれぞれの処理を高速で行うための一つのSolutionがHadoop上で機械学習をしてしまうことだと思います。Hadoop上で機械学習をするための便利なライブラリとしてJAVAベースのMahoutがあります。この記事ではMahoutによるNaiveBayes分類学習を中心としたMahoutデータの生成と使い方について紹介します。
Machine Learning With Hadoop - Yuta.Kikuchiの日記
NLTKによる分かち書き
NLTK Install
Pythonの自然言語処理ライブラリのnltkを利用して分かち書きを行います。nltkのセットアップは非常に簡単で以下のコマンドを実行するだけです。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 nltkMecab Install
形態素解析器で有名なMecabをPythonから利用できるようにします。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 installlibmecab.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の日記 ここでも書いたようにまどマギの台詞を抽出してテキストファイルに落とします。更にテキストファイルに落としたデータに対して分かち書きを行います。分かち書きをした結果をディレクトリ名とファイル名をディレクトリ人物としたパスに保存します。
#!/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/xlkSequenceFile生成
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
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の日記$ 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
Machine Learning With Hadoop
Machine Learning: A Probabilistic Perspective (Adaptive Computation and Machine Learning series)
- 作者: Kevin P. Murphy
- 出版社/メーカー: The MIT Press
- 発売日: 2012/08/24
- メディア: ハードカバー
- 購入: 1人 クリック: 26回
- この商品を含むブログを見る
Big DataのMachine Learning
Daily数百ギガバイトのAccessLogからDataMiningに必要なFeatureをかき集めるのにスタンドアロンの端末で処理を行うには時間が掛かりすぎます。バッチ処理で1日以内にUserのAccessLogを整形、必要な部分を取り出してDataMining/Machine Learningに掛けて、Userが利用するSystemに反映して行こうと考えると最初のバッチ処理で利用できる時間はそれほど多くありません。処理時間改善のためにHadoopを使い複数台のマシンに大量のログデータとバッチ処理を分散させる仕組みはBig Dataを扱う人の中では常識として利用されています。今日はBigDataをMachineLearningさせたいときの方法について調べた内容を載せます。Apache Mahout、PigのUDF、独自MapReduceの3つのうちどれかを使う事になりそうです。
Apache Mahout
- Hadoop上で実行可能な機械学習ライブラリ。ファイルをDownloadして展開するだけで利用可能。一番お手軽だが未実装のAlgorithmもあり、公開されているPatchファイルを当てるなどの対応が必要になる場合がある。
- Apache Mahout: Scalable machine learning and data mining
- Algorithms
- Classification
- Clustering
- Pattern Mining
- Regression
- Dimension reduction
- Evolutionary Algorithms
- Recommenders / Collaborative Filtering
- Vector Similarity
Pig
- Pig専用のScriptを書く事でMap/Reduceが可能。データのjoinなどもできる。Hiveのライバルプロジェクト。MachineLearningを行う場合はuser-defined functions (UDFs)を書く必要がある。MachineLearningのUDFsはほとんどWeb上に公開されていない。TwitterはPigを使って機械学習している。
- Welcome to Apache Pig!
- Large-Scale Machine Learning at Twitter
- Large-Scale Machine Learning at Twitter // Speaker Deck
training = load `training.txt' using SVMLightStorage() as (target: int, features: map[]); store training into `model/' using FeaturesLRClassifierBuilder();独自MapReduce
- 自分で機械学習を組み込んだMapReduceを書く必要がある。PythonなどではNumpyやScipyを利用したStreamingを書く事も可能だが手間がかかる。
- Map-Reduce for Machine Learning on Multicore
- Parallel Machine Learning for Hadoop/Mapreduce – A Python Example
その他
- Hama - a Bulk Synchronous Parallel computing framework on top of Hadoop
- Apache Hama is a pure BSP (Bulk Synchronous Parallel) computing framework on top of HDFS
- Hama: machine learning - ASF JIRA
- 機械学習は発展途上なようで今後に期待?!
- Hapyrus
- お会いした事は無いのですが、元会社の先輩が立ち上げているシステム。まだ使っていません。
- Tokyowebmining12
Pythonのscikit-learnでRandomForest vs SVMを比較してみた
- メディア: ペーパーバック
- クリック: 27回
- この商品を含むブログ (1件) を見る
Random Forest
Random Forestとは
機械学習の方法論の一つで決定木ベースの集団学習アルゴリズムを取り入れたものです。説明変数の依存が少ないことや学習が高速であることが特徴として挙げられています。英語サイトの方で特徴として紹介されているRFの内容について記述します。
Features
- 大きなデータに対して効率よく処理される。
- 変数の削除をすることなく入力した数千の変数を扱う事ができる。
- どの変数が分類に対して重要なのかを計算して与えてくれる。
- 木の構築処理中に一般的なエラーの偏りの無い計算を生成する。
- 高い割合でデータが誤っている時に誤りのデータを計算し、精度を保つ効果的な手法を持っている。
- アンバランスなデータが与えられたクラス群の中でエラーのバランスに対する手法を持っている。
- 生成された木は今後他のデータに適用させるために保存する事ができる。
- 変数とクラスタリング間の関係性に関する情報を計算する。
- クラスタリングに利用される隣接するケースを計算する。
- 上の特性はラベリングやクラスタリングされていないデータやはずれ値に対しても拡張する事ができる。
- 変数の相互作用を発見するための実験的な方法を推薦する。
Remarks
- RFではoverfitは存在しない。
- RFは複数の木として処理できるし、それは処理速度が速い。
- 5万のデータと100の変数を持ったデータに対して、100個の木に割り当て、800Mhzのマシンで11分で処理が終わる。
- RFではCross-Validationをする必要がない。out-of-bag (oob) エラー計算がその代わりとなる。
Mac × Pythonで利用する
env
pythonは2.7を利用します。元々設定されていたPythonは2.6.7だったのですが、install時に色々と問題がでてきたので2.7に変えます。複数のPythonを使い分けるにはpython_selectというコマンドがあったのですが、現在は使えなくなっているようです。その代わりにport select --setで切り替えます。
$ sudo port select --set python python27 $ python -V Python 2.7.2Package
MacOSX 10.6.8とPythonでRandomForestを利用するには以下のパッケージが必要になります。
※scikit-learnにはRandomForest以外のアルゴリズムも含まれているので、その他の機械学習ツールを使いたい場合も以下を参考にすると設定できると思います。
- Scientific Computing Tools For Python ― Numpy
- 数学関数を提供。Matlabのような機能を備えている。
- SciPy -
- Numpyを基礎にした統計的な関数を提供。
- scikit-learn: machine learning in Python ― scikit-learn 0.12.1 documentation
設定手順
※easy_installで上のpackageをinstallを試みましたが、scipyのinstallで失敗します。UMFPACKというパッケージが存在しないことが原因のようですが、直接的な解決方法が分かりませんでした。もし直接的な解決方法をご存知の方いましたら教えていただけると助かります。
$ sudo easy_install-2.7 scipy Searching for scipy Reading http://pypi.python.org/simple/scipy/ Reading http://www.scipy.org Download error: [Errno 61] Connection refused -- Some packages may not be found! Reading http://sourceforge.net/project/showfiles.php?group_id=27747&package_id=19531 Reading http://new.scipy.org/Wiki/Download Best match: scipy 0.11.0 Downloading http://pypi.python.org/packages/source/s/scipy/scipy-0.11.0.zip#md5=40b700ddde9ddab643b640fff7a9d753 Processing scipy-0.11.0.zip Running scipy-0.11.0/setup.py -q bdist_egg --dist-dir /tmp/easy_install-7aUbZx/scipy-0.11.0/egg-dist-tmp-phUwYf Running from scipy source directory. /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/numpy-1.6.2-py2.7-macosx-10.6-x86_64.egg/numpy/distutils/system_info.py:470: UserWarning: UMFPACK sparse solver (http://www.cise.ufl.edu/research/sparse/umfpack/) not found. Directories to search for the libraries can be specified in the numpy/distutils/site.cfg file (section [umfpack]) or by setting the UMFPACK environment variable. warnings.warn(self.notfounderror.__doc__) error: None間接的な解決方法として、ぐぐってみたら以下のようなサイトを見つけました。ScipySuperpackというものを利用してnumpy,matplotlib,scipyをinstallします。
ScipySuperpackのinstall.shを実行します。実行時に質問を聞かれますが、nで答えます。
$ curl -o install_superpack.sh "https://raw.github.com/fonnesbeck/ScipySuperpack/master/install_superpack.sh" $ sh install_superpack.sh Are you installing from a repository cloned to this machine (if unsure, answer no)? (y/n) n Cloning Scipy Superpack Cloning into ScipySuperpack... remote: Counting objects: 452, done. remote: Compressing objects: 100% (226/226), done. remote: Total 452 (delta 240), reused 433 (delta 221) Receiving objects: 100% (452/452), 332.41 MiB | 1.11 MiB/s, done. Resolving deltas: 100% (240/240), done. (略) Reading http://pypi.python.org/simple/patsy/ Reading https://github.com/pydata/patsy Best match: patsy 0.1.0 Downloading http://pypi.python.org/packages/source/p/patsy/patsy-0.1.0.zip#md5=4be1210fb5050fb83b6859fe7706b339 Processing patsy-0.1.0.zip Running patsy-0.1.0/setup.py -q bdist_egg --dist-dir /tmp/easy_install-u6TbHU/patsy-0.1.0/egg-dist-tmp-CXu4_o no previously-included directories found matching 'doc/_build' zip_safe flag not set; analyzing archive contents... Adding patsy 0.1.0 to easy-install.pth file Installed /opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/patsy-0.1.0-py2.7.egg Cleaning up DoneRandomForestの実行
RFの実行にlibsvmのサンプルデータでもあるirisを使います。LIBSVM Data: Classification, Regression, and Multi-label データに付けられたlabelとfeatureをそれぞれPythonのRandomForestClassifierに渡せるように分解し、学習データと評価データを用意します。irisは150個のサンプルデータなので75個ずつに振り分けられます。学習データをfitさせ、評価データでpredictさせます。実行してみた結果としては71/75を正確にpredictできていたので、Accuracyは94.6%でした。処理時間は0.36s user 0.17s system 99% cpu 0.536 totalと出ました。以下はテスト用のコードです。
$ wget http://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/multiclass/iris.scale $ time python rf.py predict ok id = 0 predict ok id = 1 predict ok id = 2 predict ok id = 3 predict ok id = 4 predict ok id = 5 predict ok id = 6 predict ok id = 7 predict ok id = 8 predict ok id = 9 predict ok id = 10 predict ok id = 11 predict ok id = 12 (略) predict miss id = 60 predict miss id = 61 predict ok id = 62 predict miss id = 63 predict ok id = 64 (略) python rf.py 0.36s user 0.17s system 99% cpu 0.536 total#!/usr/bin/env python # -*- coding: utf-8 -*- import re from sklearn.ensemble import RandomForestClassifier training_label = [] training_data = [] predict_label = [] predict_data = [] num = 0 file = open( './iris.scale' , 'r' ) for line in file : line = line.rstrip() node = [] label = re.search( r'^(.*?)\s', line ).group(1) for i in range(1,5) : try : pattern = r'%s' % ( str( i ) + ':(.*?\s)' ) match = re.search( pattern, line ).group(1) if match is None : node.append(0) else: node.append(match) except AttributeError : node.append(0) continue if num % 2 == 0 : training_data.append( node ) training_label.append( label ) else : predict_data.append( node ) predict_label.append( label ) num = num + 1 predict_data = training_data model = RandomForestClassifier() model.fit(training_data, training_label) output = model.predict(predict_data) for i in range( 0,len( output ) ) : str = "ok" if( int( predict_label[i] ) == int( output[i] ) ) else "miss" print "predict %s id = %d" % ( str, i )SVMの実行
同じ要領でSVMも実行してみました。kernelは線形を利用しています。結果としては68 / 75が正確にpredictされていてAccuracyは90.6%でした。処理時間は0.38s user 0.20s system 97% cpu 0.599 totalとなりました。以下にPythonのテストコードを載せます。
$ time python svm.py predict ok id = 0 predict ok id = 1 predict ok id = 2 predict ok id = 3 predict ok id = 4 predict ok id = 5 predict ok id = 6 predict ok id = 7 predict ok id = 8 predict ok id = 9 predict ok id = 10 predict ok id = 11 predict ok id = 12 (略) predict ok id = 37 predict miss id = 38 predict ok id = 39 predict ok id = 40 predict miss id = 41 predict ok id = 42 (略) python svm.py 0.38s user 0.20s system 97% cpu 0.599 total#!/usr/bin/env python # -*- coding: utf-8 -*- import re,numpy as np from sklearn import svm training_label = [] training_data = [] predict_label = [] predict_data = [] num = 0 file = open( './iris.scale' , 'r' ) for line in file : line = line.rstrip() node = [] label = re.search( r'^(.*?)\s', line ).group(1) for i in range(1,5) : try : pattern = r'%s' % ( str( i ) + ':(.*?\s)' ) match = re.search( pattern, line ).group(1) if match is None : node.append(0) else: node.append( float( match ) ) except AttributeError : node.append(0) continue if num % 2 == 0 : training_data.append( node ) training_label.append( label ) else : predict_data.append( node ) predict_label.append( label ) num = num + 1 model = svm.libsvm.fit( np.array( training_data ), np.float64( np.array( training_label ) ), kernel='linear' ) output = svm.libsvm.predict( np.array( predict_data ), *model, **{'kernel' : 'linear'} ) for i in range( 0, len(output) ) : str = "ok" if( int( predict_label[i] ) == int( output[i] ) ) else "miss" print "predict %s id = %d" % ( str, i )
比較のまとめ
- irisのデータに関してはRandomForestの方がAccuraryが良い結果が出た。
- 75件の学習/評価データに対する処理速度は若干ながらRandomForestの方が速い事が分かった。
線形予測の機械学習ツールliblinearで効果最大化のための最適な定数Cを探る
- 作者: Drew Conway,John Myles White
- 出版社/メーカー: Oreilly & Associates Inc
- 発売日: 2012/02/28
- メディア: ペーパーバック
- クリック: 63回
- この商品を含むブログを見る
liblinear
- LIBLINEAR -- A Library for Large Linear Classification
- 10秒で設定可能なlibsvmで機械学習を行う - Yuta.Kikuchiの日記
- R言語でSVM(Support Vector Machine)による分類学習 - Yuta.Kikuchiの日記
今日はliblinearを用いた機会学習の話です。今まではSVMを利用するときはkernelオプション付きのR言語のSVM/libsvm/svm-lightを利用していましたが、学習データが多い時に計算時間が何時間も掛かる事に不便を感じていました。そこでSVMのツールについて色々と調べてみたところ、線形予測に特化したliblinearの存在を知りました。公式のDocumentにもlibsvmとliblinearでの線形予測での処理時間が桁違いにliblinearの方が優れていることが記述されています。以下にliblinearの特徴を記述します。
- liblinearはinstanceや特徴が100万桁のデータを線形分離するためのtoolであり以下をサポートしています。
正則化とはOverfittingを回避するために罰則項を与える事です。種類としてはL1,L2,L1L2の3つが良く利用されるもので精度とスパース性によって異なります。L1は精度が低くスパース性が高い、L2は精度が高くスパース性が低い、L1L2は両方を取り入れ精度を高く保ちながらスパース性を高くすることです。
liblinear vs libsvm CrossValidation
liblinear、libsvmでの線形予測で統一したCrossValidationの精度と実行時間を比較します。データセットに対して5fold-cross-validationを行います。cross-validationのデータとしてはgisetteのscaleデータを利用しました。LIBSVM Data: Classification (Binary Class) 結果としてはliblinearがAccurary、処理速度ともにlibsvmを上回る結果となりました。
Tool Accurary 処理時間 liblinear 96.2119% 4.89s libsvm 96.1417% 96.22s
- liblinear
$time /home/yuta/work/liblinear/liblinear-1.91/train -v 5 gisette_scale ....* optimization finished, #iter = 45 Objective value = -0.281585 nSV = 657 ....* optimization finished, #iter = 44 Objective value = -0.268779 nSV = 677 ....* optimization finished, #iter = 45 Objective value = -0.266992 nSV = 658 ....* optimization finished, #iter = 48 Objective value = -0.270758 nSV = 663 ....* optimization finished, #iter = 48 Objective value = -0.282720 nSV = 677 Cross Validation Accuracy = 96.2119% /home/yuta/work/liblinear/liblinear-1.91/train -v 5 gisette_scale 4.89s user 1.68s system 48% cpu 13.452 total/home/yuta/work/libsvm/libsvm-3.12/svm-train -v 5 -t 0 gisette_scale .*.* optimization finished, #iter = 2876 nu = 0.000235 obj = -0.268630, rho = 0.708937 nSV = 638, nBSV = 0 Total nSV = 638 .*.* optimization finished, #iter = 2921 nu = 0.000246 obj = -0.280181, rho = 0.300795 nSV = 660, nBSV = 0 Total nSV = 660 .*..* optimization finished, #iter = 3122 nu = 0.000249 obj = -0.283893, rho = 0.352812 nSV = 674, nBSV = 0 Total nSV = 674 .*.* optimization finished, #iter = 2791 nu = 0.000239 obj = -0.272718, rho = 0.532717 nSV = 623, nBSV = 0 Total nSV = 623 .*.* optimization finished, #iter = 2837 nu = 0.000236 obj = -0.268811, rho = 0.831119 nSV = 642, nBSV = 0 Total nSV = 642 Cross Validation Accuracy = 96.1417% Positive (+1) class: precision = 0.960545 recall = 0.960545 F1 value = 0.960545 Negative (-1) class: precision = 0.962251 recall = 0.962251 F1 value = 0.962251 /home/yuta/work/libsvm/libsvm-3.12/svm-train -v 5 -t 0 gisette_scale 96.22s user 24.09s system 52% cpu 3:49.32 total
liblinearのPrecision、recall、F値を求める
libsvmでもdefaultのソースではCross-Validation時にPrecision、recall、F値を求めるコードは書かれていませんが、Patchにより差分を加える事でそれらの値を求める事が可能でした。libsvmのCross-ValidationでPrecision、Recall、F値を求めるPatch 同じようにliblinearでもpatchが無いか調べたところ、親切な人がgithubに上げてくれていました。LIBLINEARのcross validationオプションでprecision/recallを出力する ― Gist このpatchをdownloadして元のソースに当ててみます。
$ wget https://raw.github.com/gist/3341000/723cad7b81d28164bb7def833767b93a591cdca0/train.patch $ patch < train.patch $ gmake $ home/yuta/work/liblinear/liblinear-1.91/train -v 5 gisette_scale ....* optimization finished, #iter = 45 Objective value = -0.281585 nSV = 657 ....* optimization finished, #iter = 44 Objective value = -0.268779 nSV = 677 ....* optimization finished, #iter = 45 Objective value = -0.266992 nSV = 658 ....* optimization finished, #iter = 48 Objective value = -0.270758 nSV = 663 ....* optimization finished, #iter = 48 Objective value = -0.282720 nSV = 677 Cross Validation Accuracy = 96.2119% Positive (+1) class: precision = 0.962697 recall = 0.959943 F1 value = 0.961318 Negative (-1) class: precision = 0.961565 recall = 0.964212 F1 value = 0.962887
効果最大なCを探る
SVMは多次元にPlotされた座標のマージン最大化を行う分離線を求める手法です。データが奇麗に分けられるような境界線を常に求められれば問題はありませんが、ノイズやデータのオーバーラップなどの影響を受けて正確に分離が行えない事もあります。データの誤りを許さないような方法はハードマージン、ある程度データの誤りを許すがペナルティを与えるのがソフトマージンと呼ばれるものです。ソフトマージンで利用するパラメータにはスラック変数のζ(ゼータ)と外部変数のCがあります。Cパラメータは正則化と同じように誤りに対するペナルティ項です。SVMではスラック変数のゼータと外部パラメータのCの関係はCを大きくする = ゼータを小さくする、Cを小さくする = ゼータを大きくするという調整になっています。
目的関数 :
制約条件 :
Cはペナルティ項なのでCを大きくするのは制約を大きくすることを意味するのでCを無限に近づければハードマージン化させることを意味し、逆にCを小さくするとソフトマージン化させることになります。それで最適なCを求めるにはどうしたら良いか?これは総当たりの実験で求めていくしか手段が無いようです。以下のPythonコードでC=0.1〜1、1〜10の5fold-cross-validationを実行してみます。結果としてはC=0.4,0.6,0.9,3,4の時にValidation Accuracy = 96.282%という最大数を得る事が分かりました。Cを掛けた時のAccuraryが線形に変化すると予測していたのですが、どうもバラバラの結果となってしまいました。#!/usr/bin/env python # -*- coding: utf-8 -*- import os,sys filename = sys.argv[1] for i in range( 1, 11 ) : c = i print "5fold-cross-validation C=" + str( c ) os.system( '/home/yuta/work/liblinear/liblinear-1.91/train -v 5 -c ' + str( c ) + ' ' + filename ) print "\n"$ python cross-validation.py /home/yuta/work/data/gisette_scale 5fold-cross-validation C=0.1 Cross Validation Accuracy = 96.2469% 5fold-cross-validation C=0.2 Cross Validation Accuracy = 96.1417% 5fold-cross-validation C=0.3 Cross Validation Accuracy = 96.1768% 5fold-cross-validation C=0.4 Cross Validation Accuracy = 96.282% 5fold-cross-validation C=0.5 Cross Validation Accuracy = 96.2469% 5fold-cross-validation C=0.6 Cross Validation Accuracy = 96.282% 5fold-cross-validation C=0.7 Cross Validation Accuracy = 96.1768% 5fold-cross-validation C=0.8 Cross Validation Accuracy = 96.2469% 5fold-cross-validation C=0.9 Cross Validation Accuracy = 96.282% 5fold-cross-validation C=1 Cross Validation Accuracy = 96.2119% 5fold-cross-validation C=2 Cross Validation Accuracy = 96.2119% 5fold-cross-validation C=3 Cross Validation Accuracy = 96.282% 5fold-cross-validation C=4 Cross Validation Accuracy = 96.282% 5fold-cross-validation C=5 Cross Validation Accuracy = 96.1768% 5fold-cross-validation C=6 Cross Validation Accuracy = 96.2119% 5fold-cross-validation C=7 Cross Validation Accuracy = 96.2119% 5fold-cross-validation C=8 Cross Validation Accuracy = 96.2119% 5fold-cross-validation C=9 Cross Validation Accuracy = 96.2119% 5fold-cross-validation C=10 Cross Validation Accuracy = 96.2119%まとめ
そろそろ本気で機械学習の評価方法について学習するよ
- 作者: Drew Conway,John Myles White
- 出版社/メーカー: Oreilly & Associates Inc
- 発売日: 2012/02/28
- メディア: ペーパーバック
- クリック: 63回
- この商品を含むブログを見る
機械学習の評価方法について学習
機械学習初心者ですが最近業務で本格的に触り始めています。少し前までSmartPhoneのWebAppliを作ることを専門職としていたので機械学習の領域は未知な事が非常に多く、用語の意味ですら十分に理解できていません。今日は機械学習の評価方法を中心に学習(勉強)した内容を記録して行きます。例えばPrecision/Accuracy/Recallの言葉の違いやROC曲線,AUC評価などの技法といったものが話の中心になります。初心者視点で書いていますので専門性がありません。間違い等ありましたらご指摘ください。また以前にもはじめての機械学習という本のサマリーを書いたのでそちらも参照していただけると嬉しいです。
初めての機械学習理論 - Yuta.Kikuchiの日記
用語定義
初めに慣れない用語が多いので、その意味を定義します。
用語 意味 K-Fold Cross-Validation 標本をK子に分割してK-1個を学習データ、1個を評価データとして扱い、K回検定を行い推定の平均を得る Leave-One-Out Cross-Validation 標本から1つの事例を取り出して評価データとし、残りを学習データとする。全事例が1回は評価となるように検定を繰り返す。 Accuracy 正解率のこと。予測結果全体と、答えがどれぐらい一致しているかを判断する指標。計算式は下記を参照。 Precision 適合率のこと。予測を正と判断した中で、答えも正のもの。計算式は下記を参照。 Recall 再現率のこと。答えが正の中で、予測が正とされたもの。計算式は下記を参照。 F-measure F値のこと。予測精度の評価指標。PresicionとRecallの調和平均。計算式は下記を参照。 ROC曲線 Receiver Operating Characteristicのこと。縦軸にTrue Positive、横軸にFalse Positiveの割合を2次元プロットして点を線で連結した曲線 AUC Area Under the Curveのこと。ROC曲線の曲線よりしたの面積。分類器の精度評価に使う。 マイクロ平均 Nセットのテストをする場合、テストを合計してから評価値を計算。計算式は下記を参照。 マクロ平均 Nセットのテストをする場合、各セットを計算してからそれらを平均する計算。計算式は下記を参照。 True Positive 正しくPositiveと判断。予測が正解しているのでOK。 False Positive 誤ってPositiveと判断。予測が不正解なのでNG。 False Negative 誤ってNegativeと判断。予測が不正解なのでNG。 True Negative 正しくNegativeと判断。予測が正解しているのでOK。
- 事実が1 事実が-1 予測が1 True Positive(TP) False Positive(FP) 予測が-1 False Negative(FN) True Negative(TN)
AccuracyとPrecisionは相関関係になり、PresicionとRecallは逆相関の関係になるのが一般的です。PresicionとRecallともに予測が正しい事の指標ですが、数式で表すと分母が予測ベースか事実ベースかが異なります。どっちだっけという判断が非常に難しいです。
True Positive,False Positive,False Negative,True Negativeの覚え方ですが、True/Falseは正しく予測できたか、Positive/Negativeは予測が正負のどちらに判断されたかを示す内容です。False Positive,False Negativeが結果と予測が一致していないので駄目な予測ということになります。正/負という言葉が入ると少しややこしい感じがしますね。上の表では正を1、負を-1と置き換えました。
Cross-Validation
10秒で設定可能なlibsvmで機械学習を行う - Yuta.Kikuchiの日記
以前にlibsvmにてK-Fold-Cross-Validationを行いました。記事を書いた時は自分で学習データ,評価データを分割してK回学習/評価を行っていましたが、実はsvm-trainコマンドにはvオプションでK-Foldを指定でき、平均Accuracyを表示してくれるようです。libsvmと同じようなツールでsvm-lightというものがありますが、xオプションを指定するとLeave-One-Out-Cross-Validationが実現できるようです。
libsvmを用いて再度news20-binaryをsvmの線形予測を行ってみます。news20-binaryはデータ量が多く学習に時間がかかりそうだったので半分のデータを利用しました。今回はCross-Validationをvオプションで行う事とPrecision,Recall,F値を求めます。通常のlibsvmではこれらを計算できないのでsleepy_yoshiさんが作られているpatchを当てます。LibSVMのcross validationオプションでprecision/recallを出力する - 睡眠不足?! 注しなければいけないことはCross-Validationを実行するとmodelは作成されません。K-Foldを5と指定し、Cross Validation Accuracy = 84.21%を得る事ができました。$ wget wget http://sleepyheads.jp/software/svm-train.patch $ patch < svm-train.patch $ gmake clean $ gmake $ wget http://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/binary/news20.binary.bz2 $ bzip2 -d news20.binary.bz2 $ cat news20.binary +1 103:0.250000 279:0.250000 291:0.250000 5757:0.250000 16311:0.250000 16314:0.250000 30408:0.250000 85418:0.250000 -1 40:0.041065 75:0.041065 89:0.041065 97:0.041065 103:0.041065 108:0.041065 114:0.041065 149:0.041065 170:0.041065 171:0.041065 174:0.041065 $ grep '^\-1' news20.binary | head -n 5000 > train.txt $ grep '^\+1' news20.binary | head -n 5000 >> train.txt $ svm-train -v 5 train.txt Cross Validation Accuracy = 84.21% Positive (+1) class: precision = 0.7466 recall = 0.922868 F1 value = 0.825428 Negative (-1) class: precision = 0.9376 recall = 0.787238 F1 value = 0.855865svmlightでも同じようなことをやってみます。libsvmとsvmlightの入力データ形式は互換性があるようです。まずはinstallするところから進めます。libsvmと同様にsvmlightも圧縮ファイルを解凍してgmakeするだけでsvm_learn,svm_classifyコマンドが生成されます。上で利用したtrain.txtとpredict.txtをそれぞれ用意してAccuracy/Preciseion/Recallの値を見てみます。svm_learnに少し時間がかかりましたが、Accuracy: 69.25% Precision:91.67% Recall:42.35%となりました。
$ wget http://download.joachims.org/svm_light/current/svm_light.tar.gz $ tar -xzf svm_light.tar.gz $ gmake $ grep '^\-1' news20.binary | tail -n 4000 > predict.txt $ grep '^\+1' news20.binary | tail -n 4000 >> predict.txt $ svm_learn -x 1 train.txt model $ svm_classify predict.txt model output (略) Leave-one-out estimate of the error: error=1.89% Leave-one-out estimate of the recall: recall=98.28% Leave-one-out estimate of the precision: precision=97.95% Actual leave-one-outs computed: 561 (rho=1.00) Runtime for leave-one-out in cpu-seconds: 2652.56 $ svm_classify predict.txt model output Reading model...OK. (4120 support vectors read) Classifying test examples..100..200..300..400..500..600..700..800..900..1000..1100..1200..1300..1400..1500..1600..1700..1800..1900..2000..2100..2200..2300..2400..2500..2600..2700..2800..2900..3000..3100..3200..3300..3400..3500..3600..3700..3800..3900..4000..4100..4200..4300..4400..4500..4600..4700..4800..4900..5000..5100..5200..5300..5400..5500..5600..5700..5800..5900..6000..6100..6200..6300..6400..6500..6600..6700..6800..6900..7000..7100..7200..7300..7400..7500..7600..7700..7800..7900..8000..done Runtime (without IO) in cpu-seconds: 0.15 Accuracy on test set: 69.25% (5540 correct, 2460 incorrect, 8000 total) Precision/recall on test set: 91.67%/42.35%
ROC曲線、AUC
ROC曲線は2値分類問題を解く場合に利用するもので、TruePositiveとFalsePositiveの割合をplotし、分類器の精度評価を行う目的で利用します。通常は縦軸にTruePositive、横軸にFalsePositiveを定義します。精度が高いことを証明するために理想の曲線としては横軸の値が小さい段階で縦軸の値が高く示し、そこから水平に伸びて行く状態です。縦軸には 横軸にはをplotします。AUCとはROC曲線より下に示される面積であるのでROCが縦軸に高く引っ張られているほど面積も大きくなります。ランダムの場合ROC曲線が[0,0],[1,1]への直線となり、AUCは1*1/2 = 0.5となります。
LIBSVM Tools ROC Curve for Binary SVMという項目にlibsvm向けのMatlabとPython用のコマンドスクリプトが用意されています。これを利用してROC曲線を描きAUCを算出してみます。残念ながら手元にはMatlabが無いのでPythonで試してみます。 http://www.csie.ntu.edu.tw/~cjlin/libsvmtools/roc/plotroc.py このplotroc.pyを取得します。この実行にはlibsvmを解凍した時に生成されるpythonディレクトリ以下のsvm.py、svmutil.pyというファイルを必要とします。python scriptを通してもlibsvmのオプションは基本的にそのまま利用できます。$ wget http://www.csie.ntu.edu.tw/~cjlin/libsvmtools/roc/plotroc.py $ python plotroc.py $ python plotroc.py -v 5 train.txt上を一度実行したのですが、実行環境のmemory不足で以下のようなエラーがでました。解決方法は現在調査中ですがスマートな対応はできないかもしれないです。ここではtrain.txtのデータ量を半分にする事で再度plotをしました。今度は正常に画像が出力され、AUC=0.7222という値を得る事ができました。ランダムが0.5なのでそれよりは良い結果を得る事ができました。
optimization finished, #iter = 3973 nu = 0.993250 obj = -7945.807458, rho = 0.999930 nSV = 7946, nBSV = 7946 Total nSV = 7946 Accuracy = 48.65% (973/2000) (classification) Traceback (most recent call last): File "plotroc.py", line 219, in <module> main() File "plotroc.py", line 216, in main plot_roc(deci, train_y, output_file, output_title) File "plotroc.py", line 167, in plot_roc g = gnuplot(output) File "plotroc.py", line 63, in __init__ self.__dict__['iface'] = popen(cmdline,'w') OSError: [Errno 12] Cannot allocate memory Exception AttributeError: "gnuplot instance has no attribute 'iface'" in ignored -----データ量を半分にしてplot $ head -n 2500 train.txt > train2.txt $ tail -n 2500 train.txt >> train2.txt $ mv train2.txt train.txt $ python plotroc.py -v 5 train.txtplotされた座標のデータを取得したい場合はplotroc.pyに対して以下の行を追加する事でできます。またAUCも画像で出力するのではなく標準でも出力するように修正しました。
$ diff -u plotroc.py plotroc2.py --- plotroc.py 2010-11-11 18:45:28.000000000 +0900 +++ plotroc2.py 2012-09-09 17:58:31.000000000 +0900 @@ -150,6 +150,7 @@ tp+=1 else: fp+=1 + print fp/neg,tp/pos xy_arr.append([fp/neg,tp/pos]) #area under curve @@ -159,6 +160,7 @@ if x != prev_x: aoc += (x - prev_x) * y prev_x = x + print "AUC = %0.4f" % aoc #begin gnuplot if title == None: 0.996 1.0 0.9964 1.0 0.9968 1.0 0.9972 1.0 0.9976 1.0 0.998 1.0 0.9984 1.0 0.9988 1.0 0.9992 1.0 0.9996 1.0 1.0 1.0 AUC = 0.7222ここで気づいたのですが、libsvmはデフォルトでRBFを選択しているのでtオプションで線形を選択して再度実行してみます。結果的にはAUCが0.9973という恐ろしい値を得る事ができました。やり方が間違っていないか逆に心配です。
$ python plotroc.py -t 0 -v 5 -h 0 train.txt
Support Vector Machinesを用いた「魔法少女まどか☆マギカ」人物予測モデル
- 作者: 高村大也,奥村学
- 出版社/メーカー: コロナ社
- 発売日: 2010/07
- メディア: 単行本
- 購入: 13人 クリック: 235回
- この商品を含むブログ (39件) を見る
人物予測モデル
記事のタイトルがだいぶ固い内容になっていまいましたがやりたい事はとても簡単です。過去に発せられたまど☆マギ台詞の形態素を学習し、予測モデルを作成します。その後に未分類の形態素のデータセットを与えた時にどれだけ人物のラベル付けが正しく行われたかを評価します。予測モデルの対象となる人物は鹿目まどか/暁美ほむら/美樹さやか/キュゥべえ/佐倉杏子/巴マミの合計6名です。機械学習にはSVMを利用します。先に実験の結果をお伝えしておくと、台詞の形態素ベクトルでは十分なマルチラベリングができていません。それでもこの記事が気になる方は読み進めてください。処理手順の詳細は以下の通りです。
まど☆マギ台詞の収集
魔法少女まどか☆マギカ WIKI - ネタバレ考察/台詞集
上のWIKIの台詞を利用させてもらっています。※承諾は得ていません。
Pythonコードで各登場人物の台詞を取得します。Webページのスクレイピングによる抽出です。実行すると各登場人物ファイルにデータを落とし込みます。#!/usr/bin/env python # -*- coding: utf-8 -*- import sys,re,urllib,urllib2 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()
形態素解析と形態素ベクトル化
Mecabによる形態素解析
MeCab: Yet Another Part-of-Speech and Morphological Analyzer
形態素解析にはMecabを利用します。Mecabの設定完了後、import Mecabとmecab.parseToNodeを指定すると単語の形態素分解が出来ます。試しに文書をスペース区切りの分かち書きします。上で収集した台詞をdata/all.txtというファイルにまとめます。そして以下のPythonコードを実行します。#!/usr/bin/env python # -*- coding: utf-8 -*- import sys reload(sys) sys.setdefaultencoding('utf-8') import MeCab mecab = MeCab.Tagger('-Ochasen') allfile = 'data/all.txt' data = open( allfile ).read() node = mecab.parseToNode( data ) phrases = node.next while phrases: try: print node.surface + " ", node = node.next except AttributeError: break暁美 ほ むら です 。 よろしく お願い し ます 東京 の 、 ミッション 系 の 学校 よ やっ て 無かっ た わ ごめんなさい 。 何だか 緊張 し すぎ た みたい で 、 ちょっと 、 気分 が 。 保健 室 に 行か せ て 貰える かしら いえ 、 おか まい なく 。 係 の 人 に お願い し ます から 鹿目 まどか さん 。形態素ID付け/ベクトル化
SVMはベクトル空間でのマージン最大化を行うので、形態素そのままだと機械学習ができません。そこで形態素に対して整数のIDを割り当て、1行の台詞で何回出現したかを記録します。libsvmに最終的に渡したいデータは
正解ラベル<スペース>形態素ID:出現回数<スペース>形態素ID:出現回数
という形式にします。正解ラベルは人物ID、形態素IDは全形態素でユニークなIDを示します。以下のコードを実行するとlibsvmへの入力データを生成します。#!/usr/bin/env python # -*- coding: utf-8 -*- import sys reload(sys) sys.setdefaultencoding('utf-8') import MeCab mecab = MeCab.Tagger('-Ochasen') names = { 'homura' : 1, 'kyoko' : 2, 'kyube' : 3, 'madoka' : 4, 'mami' : 5, 'sayaka' : 6 } allfile = './data/all.txt' data = open( allfile ).read() node = mecab.parseToNode( data ) words = {} num = 0; phrases = node.next while phrases: try: k = node.surface k = k.strip().rstrip() if k in words: pass else: words[k] = num; num = num + 1 node = node.next except AttributeError: break for i in names.keys(): file = './data/' + i + '.txt' for line in open( file, 'r' ): line = line.strip().rstrip() n = mecab.parseToNode( line ) p = n.next attrs = {} while p: try: k = n.surface if k not in words: break id = words[k] if id in attrs: attrs[id] = attrs[id] + 1 else: attrs[id] = 1 n = n.next except AttributeError: break print names[i], for ak in sorted( attrs.keys() ): print str(ak) + ":" + str(attrs[ak]), print6 0:2 12:1 46:1 199:1 2188:1 2452:1 6 0:2 54:1 757:1 1992:1 2453:1 6 0:2 5:1 8:2 11:1 46:1 60:1 62:1 84:2 668:1 1329:1 1974:1 2005:1 2454:1 2455:1 6 0:2 84:1 872:1 2456:1 2457:1 6 0:2 2:1 4:1 5:1 8:1 26:1 33:1 44:1 46:1 54:2 66:1 83:1 100:1 144:3 353:1 530:1 1673:1 1992:1 2188:1 2458:1 2459:1 2460:1 2461:1 2462:1 2463:1 2464:1 6 0:2 12:1 20:1 44:2 54:1 62:1 71:1 84:1 110:1 144:2 189:1 292:1 406:1 418:1 489:2 572:1 1974:1 2140:1 2188:2 2465:1 2466:1 2467:1 2468:1 2469:1 2470:1 6 0:2 11:2 33:2 46:1 62:1 69:1 84:1 85:1 93:1 107:1 132:1 189:1 201:1 209:1 489:3 969:1 2188:2 2378:1 2453:1 2466:1 2471:1 2472:1
機械学習
2Fold-Cross-Validation
形態素をベクトル化したデータを3つに分割して、それぞれを学習/評価データとして利用します。今回はデータも少ないので2つに分けています。上で出力したデータをvector.txtとして保存した場合、以下のスクリプトでデータを分離する事が出来ます。shuffle分割したファイルを別名で保存し直して、libsvmのsvm-trainコマンドに掛けます。その結果、予測Modelのファイルが生成されます。
$ perl -MList::Util=shuffle -e 'print shuffle(<>)' < vector.txt | split -l 945 $ mv xaa data1.txt $ mv xab data2.txt $ svm-train data1.txt * optimization finished, #iter = 136 nu = 0.739927 obj = -200.401126, rho = 0.908119 nSV = 214, nBSV = 189 * optimization finished, #iter = 187 nu = 0.521964 obj = -201.006920, rho = 0.935477 nSV = 221, nBSV = 180 * optimization finished, #iter = 141 nu = 0.824490 obj = -200.481473, rho = 0.917041 nSV = 214, nBSV = 189 $ svm-train data2.txt * optimization finished, #iter = 178 nu = 0.414698 obj = -157.645809, rho = 0.977467 nSV = 196, nBSV = 128 * optimization finished, #iter = 121 nu = 0.774510 obj = -157.307372, rho = 0.989609 nSV = 172, nBSV = 142 * optimization finished, #iter = 135 nu = 0.661088 obj = -157.307427, rho = 0.963986 nSV = 181, nBSV = 141 * $ ls 合計 492 drwxr-xr-x 2 yuta yuta 4096 9月 2 00:27 . drwxr-xr-x 5 yuta yuta 4096 9月 2 00:00 .. -rw-r--r-- 1 yuta yuta 69615 9月 2 00:19 data1.txt -rw-r--r-- 1 yuta yuta 84900 9月 2 00:27 data1.txt.model -rw-r--r-- 1 yuta yuta 66225 9月 2 00:19 data2.txt -rw-r--r-- 1 yuta yuta 81545 9月 2 00:27 data2.txt.model -rw-r--r-- 1 yuta yuta 135840 9月 1 23:52 vector.txtprediction
$ svm-predict data2.txt data1.txt.model output > accuracy1.txt Accuracy = 31.9915% (302/944) (classification) $ svm-predict data1.txt data2.txt.model output2 > accuracy2.txt Accuracy = 30.2646% (286/945) (classification)大きな問題として学習データが少な過ぎて評価がしづらい状態になりました。またoutputファイルの中身を見てみると全て4(鹿目まどか)というラベル付けがされてしまっていて評価に失敗しています。これは学習データ量に4のラベルが偏っていたためと考えます。全体的な話だとAccuracyが32%、30%となっており登場人物6名のマルチラベル問題なので1/6= 16%と比較すると倍以上の正解度と考えられますが、前述したように学習自体が正しく行われていないので再度やり直します。
学習データを整えて再度prediction
各登場人物の台詞ベクトルデータを100件ずつ学習データ、評価データに使います。巴マミの台詞データは残念ながら200件データが無かったのでラベリングの対象から外しました。よって5人分のラベリングになります。predictの結果は以下の通りで、Accuracyが53.8%、32.2%という結果でどちらとも1/5=20%の数値は超えています。Model1,Model2で予測されたラベルの個数を見てみました。Model1はそれなりにデータが分散していますが、Model2では佐倉杏子に偏ってしまい、学習が失敗していることが分かります。
$ svm-predict data2.txt data1.txt.model output Accuracy = 53.8% (269/500) (classification) $ svm-predict data1.txt data2.txt.model output2 Accuracy = 32.2% (161/500) (classification)
人物 Model1 Model2 暁美ほむら 109 22 佐倉杏子 29 437 キュゥべえ 53 15 鹿目まどか 273 12 美樹さやか 36 14
10秒で設定可能なlibsvmで機械学習を行う
Support Vector Machines (Information Science and Statistics)
- 作者: Ingo Steinwart,Andreas Christmann
- 出版社/メーカー: Springer
- 発売日: 2008/08/29
- メディア: ハードカバー
- クリック: 17回
- この商品を含むブログを見る
libsvm
- LIBSVM -- A Library for Support Vector Machines
- R言語でSVM(Support Vector Machine)による分類学習 - Yuta.Kikuchiの日記
前回RでのSVMを簡単に紹介しましたが、今日はlibsvmを利用したirisの分類学習を行いたいと思います。libsvmは導入がめちゃくちゃ簡単なところが売りだと思います。zipをlibsvmサイトからdownloadして展開してgmakeで設定完了です。
設定
$ wget "http://www.csie.ntu.edu.tw/~cjlin/cgi-bin/libsvm.cgi?+http://www.csie.ntu.edu.tw/~cjlin/libsvm+zip" $ unzip libsvm-3.12.zip $ cd libsvm-3.12 $ gmake $ ls -rw-r--r-- 1 yuta yuta 1497 1月 31 2012 COPYRIGHT -rw-r--r-- 1 yuta yuta 72186 4月 1 07:17 FAQ.html -rw-r--r-- 1 yuta yuta 732 1月 2 2012 Makefile -rw-r--r-- 1 yuta yuta 1087 9月 12 2010 Makefile.win -rw-r--r-- 1 yuta yuta 27332 2月 3 2012 README -rw-r--r-- 1 yuta yuta 27670 7月 12 2003 heart_scale drwxr-xr-x 3 yuta yuta 4096 4月 1 07:18 java drwxr-xr-x 2 yuta yuta 4096 10月 30 2011 matlab drwxr-xr-x 2 yuta yuta 4096 3月 22 12:25 python -rwxr-xr-x 1 yuta yuta 67413 8月 29 07:53 svm-predict -rw-r--r-- 1 yuta yuta 5381 2月 5 2011 svm-predict.c -rwxr-xr-x 1 yuta yuta 15650 8月 29 07:53 svm-scale -rw-r--r-- 1 yuta yuta 7042 5月 28 2011 svm-scale.c drwxr-xr-x 5 yuta yuta 4096 2月 3 2012 svm-toy -rwxr-xr-x 1 yuta yuta 71912 8月 29 07:53 svm-train -rw-r--r-- 1 yuta yuta 8891 5月 27 2011 svm-train.c -rw-r--r-- 1 yuta yuta 63412 12月 26 2011 svm.cpp -rw-r--r-- 1 yuta yuta 434 9月 12 2010 svm.def -rw-r--r-- 1 yuta yuta 3129 2月 3 2012 svm.h -rw-r--r-- 1 yuta yuta 93208 8月 29 07:53 svm.o drwxr-xr-x 2 yuta yuta 4096 2月 24 2012 tools drwxr-xr-x 2 yuta yuta 4096 3月 16 00:44 windowsscale,train,predictコマンド
gmakeを行うと以下のコマンドが生成されます。これらのコマンドに対して学習データ,学習Model,評価データを与える事によりSVMによる機械学習が実現できます。各種コマンドの使い方については以下のサイトが詳しいと思います。またコマンドに対してaliasを貼っておきます。
- svm-scale : データの正規化を行うコマンド
- svm-train : 学習データからModelを生成するコマンド
- svm-predict:評価データとModelから分類とaccuracyを導きだすコマンド
alias svm-predict=/home/yuta/work/libsvm/libsvm-3.12/svm-predict alias svm-scale=/home/yuta/work/libsvm/libsvm-3.12/svm-scale alias svm-train=/home/yuta/work/libsvm/libsvm-3.12/svm-trainirisデータの学習
- LIBSVM Data: Classification, Regression, and Multi-label
- iris.scale
- UCI Machine Learning Repository: Iris Data Set
R言語には標準でirisのデータが備わっていましたが、libsvmのdataformatに従ったサンプルも以下にあります。libsvmに対しての学習にはiris.scaleのデータはiris.scaleを利用します。Iris Setosa、Iris Versicolour 、Iris Virginicaといった3つのアヤメ種別(class)と蕚片の長さ/幅、花びらの長さ/幅といった4つの特徴(attribute)を持つものです。種別に対しては1から3のラベルを貼り、各種特徴にも特徴番号を付け、-1〜1の正規化した特徴量を持たせています。以下にデータ取得から学習までのコマンドを記載します。svm-trainコマンドを実行するとiris.scale.modelというmodelファイルが生成されます。
$ wget "http://www.csie.ntu.edu.tw/~cjlin/libsvmtools/datasets/multiclass/iris.scale" $ less iris.scale 1 1:-0.555556 2:0.25 3:-0.864407 4:-0.916667 1 1:-0.666667 2:-0.166667 3:-0.864407 4:-0.916667 1 1:-0.777778 3:-0.898305 4:-0.916667 1 1:-0.833333 2:-0.0833334 3:-0.830508 4:-0.916667 1 1:-0.611111 2:0.333333 3:-0.864407 4:-0.916667 1 1:-0.388889 2:0.583333 3:-0.762712 4:-0.75 1 1:-0.833333 2:0.166667 3:-0.864407 4:-0.833333 1 1:-0.611111 2:0.166667 3:-0.830508 4:-0.916667 1 1:-0.944444 2:-0.25 3:-0.864407 4:-0.916667 1 1:-0.666667 2:-0.0833334 3:-0.830508 4:-1 1 1:-0.388889 2:0.416667 3:-0.830508 4:-0.916667 1 1:-0.722222 2:0.166667 3:-0.79661 4:-0.916667 $ svm-train iris.scale * optimization finished, #iter = 12 nu = 0.092569 obj = -5.138435, rho = -0.062041 nSV = 11, nBSV = 8 * optimization finished, #iter = 25 nu = 0.048240 obj = -2.538201, rho = 0.016007 nSV = 8, nBSV = 2 * optimization finished, #iter = 36 nu = 0.447449 obj = -32.467419, rho = 0.105572 nSV = 46, nBSV = 42 Total nSV = 58 $ ls drwxr-xr-x 2 yuta yuta 4096 8月 29 09:03 . drwxr-xr-x 4 yuta yuta 4096 8月 29 08:57 .. -rw-r--r-- 1 yuta yuta 6954 6月 8 2005 iris.scale -rw-r--r-- 1 yuta yuta 3226 8月 29 09:03 iris.scale.modelModelによる評価
予測はsvm-predictコマンドで行います。ここでは学習データで使ったデータをそのまま予測用データとして利用します。svm-predictの第一引数に評価データ、第二引数に学習で生成したmodel、第三引数にラベル付けの結果ファイルを指定します。標準出力でaccuracyが出てくるのでそれをファイルにリダイレクトしておきます。個々の結果ではaccuracyが97%と出ました。
$ svm-predict iris.scale iris.scale.model iris.scale.output > accuracy.txt $ less accuracy.txt Accuracy = 97.3333% (146/150) (classification)K-分割交差検定
上の評価ではaccuracyが97%と出ていますが、学習データと評価データが同じでは本来のModel精度が分からないので、まずは学習データと評価データを分離します。ここでは一般的なCross Validationにしたがって150個のirisサンプルを50個ずつに分割します。50個のデータセットをA,B,Cというファイルで定義した場合、学習と評価を3パターンで試します。交差検定を"K-fold cross-validation"と呼びますが、ここではK=3となります。
- 学習(A,B) 評価(C)
- 学習(A,C) 評価(B)
- 学習(B,C) 評価(A)
行を完全にrandomでshuffleして100行の学習データ/50行の評価データを作成します。
$ perl -MList::Util=shuffle -e 'print shuffle(<>)' < iris.scale | split -l 50 $ cat xaa xab | wc -l $ cat xaa xab > 1.train.txt $ cat xaa xac > 2.train.txt $ cat xab xac > 3.train.txt $ mv xac 1.predict.txt $ mv xab 2.predict.txt $ mv xaa 3.predict.txt続いて学習と予測、accuracy算出までをやります。下の結果から分かるようにAccuracyの算出が94%、96%、98%といずれも高い数値が出ました。Cross Validationでもかなり高い確率で予測が出来ていると言えます。
$ svm-train 1.train.txt $ svm-train 2.train.txt $ svm-train 3.train.txt * optimization finished, #iter = 21 nu = 0.065188 obj = -2.237066, rho = 0.057124 nSV = 7, nBSV = 1 * optimization finished, #iter = 30 nu = 0.558458 obj = -26.839488, rho = -0.084482 nSV = 39, nBSV = 34 * optimization finished, #iter = 8 nu = 0.119403 obj = -4.403712, rho = 0.039139 nSV = 10, nBSV = 7 Total nSV = 49 * optimization finished, #iter = 22 nu = 0.070101 obj = -2.437480, rho = 0.018715 nSV = 8, nBSV = 2 * optimization finished, #iter = 17 nu = 0.443524 obj = -20.533308, rho = -0.040417 nSV = 30, nBSV = 28 * optimization finished, #iter = 12 nu = 0.128258 obj = -4.819891, rho = -0.074851 nSV = 11, nBSV = 8 Total nSV = 44 * optimization finished, #iter = 8 nu = 0.129629 obj = -4.619123, rho = 0.001523 nSV = 11, nBSV = 8 * optimization finished, #iter = 9 nu = 0.067031 obj = -2.349066, rho = 0.175743 nSV = 9, nBSV = 3 * optimization finished, #iter = 39 nu = 0.506626 obj = -27.356803, rho = 0.120588 nSV = 38, nBSV = 33 Total nSV = 50 $ svm-predict 1.predict.txt 1.train.txt.model 1.output.txt > 1.accuracy.txt $ svm-predict 2.predict.txt 2.train.txt.model 2.output.txt > 2.accuracy.txt $ svm-predict 3.predict.txt 3.train.txt.model 3.output.txt > 3.accuracy.txt $ less 1.accuracy.txt Accuracy = 94% (47/50) (classification) $ less 2.accuracy.txt Accuracy = 96% (48/50) (classification) $ less 3.accuracy.txt Accuracy = 98% (49/50) (classification)