テキスト分類の流れ
前回は理論編として、
今回も、
今回取り上げるMahoutのナイーブベイズ実装を用いたテキスト分類のおもな流れは、
- 形態素解析器を用いて、
学習データの各文章を分かち書く - 分かち書きされた学習データをシーケンスファイルへ変換する
- シーケンスファイルをベクトルファイルへ変換する
- ベクトルファイルの学習データを用いてモデルを作成する
- 生成したモデルの精度をテストする
- モデルを用いてテキスト分類を行う
サンプルデータを準備する
今回の学習データには、
サンプルデータは、
今回のサンプルデータは、
ディレクトリ構成は以下のようになっており、
- learning-data/
food-negative ⇒ 味に関する否定的な回答 - learning-data/
food-positive ⇒ 味に関する肯定的な回答 - learning-data/
price-negative ⇒ 価格に関する否定的な回答 - learning-data/
price-positive ⇒ 価格に関する肯定的な回答 - learning-data/
service-negatie ⇒ サービスに関する否定的な回答 - learning-data/
service-positive ⇒ サービスに関する肯定的な回答 - learning-data/
else ⇒ 上記いずれにも該当しないその他の回答
今回のサンプルデータは学習データとして利用するため、
テキストの分かち書きを行う
前回解説したように、
しかし、
- 分かち書き前
- ハンバーグが中まで火が通ってなかった。
- 分かち書き後
- ハンバーグ が 中 まで 火 が 通っ て なかっ た 。
日本語の分かち書きの機能はMahoutには用意されていないため、
- wakati-learning-data/
food-negative - ⇒味に関する否定的な回答の分かち書き済みテキスト
- wakati-learning-data/
food-positive - ⇒味に関する肯定的な回答の分かち書き済みテキスト
(以下、省略)
なお、
テキストファイルからシーケンスファイルを生成する
Mahoutのナイーブベイズ実装で利用する学習データのファイルフォーマットはテキストファイルに対応しておらず、
そのため、
mahout seqdirectory -i file:///home/yamakatu/gihyo-mahout-nb-sample/wakati-learning -o gihyo-mahout-nb-sample/seq-learning
seqdirectoryコマンドには、
- --input (-i) ⇒ 入力データ
- --output (-o) ⇒ シーケンスファイルの出力先
- --chunkSize (-chunk) ⇒ ファイルの分割サイズ
(デフォルトは64Mbytes) - --keyPrefix (-prefix) ⇒ 指定した文字列がKeyのprefixに利用される
- --charset (-c) ⇒ 文字コードを指定
(デフォルトはUTF-8)
生成されたシーケンスファイルの内容は、
mahout seqdumper -i gihyo-mahout-nb-sample/seq-learning
無事、
(略) Key: /price-positive/65.txt: Value: ここ の コーヒー は 、 注文 が 入っ て から エスプレッソマシーン で 入れ て くれ て いる ので 、 とても コス パ が いい と 思う 。 Key: /price-positive/62.txt: Value: ランチ セット は お 得 で 良い と 思う 。 (略)
出力された内容からわかるように、
シーケンスファイルからベクトルファイルを作成する
次に、
mahout seq2sparse -i gihyo-mahout-nb-sample/seq-learning -o gihyo-mahout-nb-sample/sparse-learning -a org.apache.lucene.analysis.WhitespaceAnalyzer
今回は最初に分かち書きを済ませているため、
成功すると、
[yamakatu@localhost data]$ hadoop fs -ls gihyo-mahout-nb-sample/sparse-learning Found 7 items drwxr-xr-x - yamakatu supergroup 0 2013-10-03 10:35 /user/yamakatu/gihyo-mahout-nb-sample/sparse-learning/df-count -rw-r--r-- 1 yamakatu supergroup 1860 2013-10-03 10:34 /user/yamakatu/gihyo-mahout-nb-sample/sparse-learning/dictionary.file-0 -rw-r--r-- 1 yamakatu supergroup 1993 2013-10-03 10:35 /user/yamakatu/gihyo-mahout-nb-sample/sparse-learning/frequency.file-0 drwxr-xr-x - yamakatu supergroup 0 2013-10-03 10:35 /user/yamakatu/gihyo-mahout-nb-sample/sparse-learning/tf-vectors drwxr-xr-x - yamakatu supergroup 0 2013-10-03 10:36 /user/yamakatu/gihyo-mahout-nb-sample/sparse-learning/tfidf-vectors drwxr-xr-x - yamakatu supergroup 0 2013-10-03 10:34 /user/yamakatu/gihyo-mahout-nb-sample/sparse-learning/tokenized-documents drwxr-xr-x - yamakatu supergroup 0 2013-10-03 10:34 /user/yamakatu/gihyo-mahout-nb-sample/sparse-learning/wordcount
tf-vectorsとtfidf-vectorsの内容は、
mahout vectordump -i gihyo-mahout-nb-sample/sparse-learning/tfidf-vectors
tf-vectorsとtfidf-vectors以外のファイルは、
ベクトルファイルからモデルを生成する
ベクトルファイルの生成が完了すれば、
モデルの生成には、
mahout trainnb -i gihyo-mahout-nb-sample/sparse-learning/tfidf-vectors -o gihyo-mahout-nb-sample/model -el -li gihyo-mahout-nb-sample/labelindex
trainnbコマンドのおもなオプションとして、
- --input (-i) ⇒ 学習データ
- --output (-o) ⇒ モデルの出力先
- --extractLabels (-el) ⇒ ラベルインデックスを作成
- --trainComplementary (-c) ⇒ complement naive bayesを利用
- --labelIndex (-li) ⇒ ラベルインデックスのパスを指定
-elオプションにより作成したラベルインデックスの内容は、
mahout seqdumper -i gihyo-mahout-nb-sample/labelindex
以下のように、
Key: else: Value: 0 Key: food-negative: Value: 1 Key: food-positive: Value: 2 Key: price-nagative: Value: 3 Key: price-positive: Value: 4 Key: service-negative: Value: 5 Key: service-positive: Value: 6
このラベルインデックスは、
モデルの精度をテストする
モデルを生成した後には、
今回はサンプルのため、
mahout testnb -i gihyo-mahout-nb-sample/sparse-learning/tfidf-vectors -o gihyo-mahout-nb-sample/test -m gihyo-mahout-nb-sample/model -l gihyo-mahout-nb-sample/labelindex
testnbのおもなオプションには、
- --input (-i) ⇒ テスト対象のデータ
- --output (-o) ⇒ テスト結果の出力先
- --model (-m) ⇒ 利用するモデル
- --testComplementary (-c) ⇒ complement naive bayesを利用
- --labelIndex (-li) ⇒ ラベルインデックスのパスを指定
以下のような結果が出力されましたでしょうか。
======================================================= Summary ------------------------------------------------------- Correctly Classified Instances : 57 87.6923% Incorrectly Classified Instances : 8 12.3077% Total Classified Instances : 65 ======================================================= Confusion Matrix ------------------------------------------------------- a b c d e f g <--Classified as 10 0 0 0 0 1 0 | 11 a = else 0 4 1 0 0 0 0 | 5 b = food-negative 0 0 4 0 0 0 0 | 4 c = food-positive 0 0 1 5 0 0 1 | 7 d = price-nagative 0 0 0 0 6 0 0 | 6 e = price-positive 0 3 1 0 0 17 0 | 21 f = service-negative 0 0 0 0 0 0 11 | 11 g = service-positive
実行結果の見方は、
モデルを用いて分類する
これまでの手順で、
これまでの手順はMahoutが用意しているコマンドで実現できましたが、
以降、
モデルを読み込む
ます、
model = NaiveBayesModel.materialize(new Path(modelFilePath), conf);
(省略)
StandardNaiveBayesClassifier classifier = new StandardNaiveBayesClassifier(model);
complement naive bayesを用いる場合は、
Vectorの次元数を決める
次にVectorの次元数を決める必要があります。今回の場合は、
単語の数は、
dicIterator = new SequenceFileIterator<Writable, Writable>(new Path(dictionaryFilePath), true, conf);
(省略)
HashMap<String, Integer> wordIDMap = new HashMap<String, Integer>();
while (dicIterator.hasNext()) {
Pair<?, ?> record = dicIterator.next();
String word = record.getFirst().toString();
Integer wordID = Integer.valueOf(record.getSecond().toString());
wordIDMap.put(word, wordID);
}
//出現する単語数をベクトルの次元数とする
int wordNum = wordIDMap.size();
これで次元数が求まりましたので、
//ベクトル生成
Vector vector = new RandomAccessSparseVector(wordNum);
各単語の出現回数をベクトルの各次元の重みとする
次に分類対象のドキュメントにおける各単語の出現回数を、
ここで、
for(String word : targetDocWordList){ //targetDocWordListは分類対象に含まれているすべての単語を格納
if(wordIDMap.get(word) == null){
continue;
}
int wordID = wordIDMap.get(word);
vector.setQuick(wordID, vector.get(wordID) + 1);
}
ここまでで、
分類する
最後に、
//分類実行
Vector result = classifier.classifyFull(vector);
分類結果には、
下記の例では、
for (int i = 0; i < result.size(); i++){
System.out.println("label id:"+i+" probability:"+result.get(i));
}
単純に考えれば、
しかし実際には、
- 最も確率が高いクラスと2番目に確率が高いクラスとの確率の差が僅差な場合
- 最も確率が高いクラスの確率がそれほど高くない場合
以上のような手順で、
最後に
今回で本連載は終了となります。これまで、
まだMahoutは現時点での最新バージョンが0.
最後になりましたが、