読者です 読者をやめる 読者になる 読者になる

kanetaiの二次記憶装置

プログラミングに関するやってみた、調べた系のものをQitaに移して、それ以外をはてブでやる運用にしようと思います。http://qiita.com/kanetai

MeCab

MeCab (和布蕪)

オープンソース形態素解析エンジン。条件付き確率場(CRF: Conditional Random Fields)でパラメータ推定されており、比較的高速な形態素解析が可能。MeCab導入の際、参考にしたページのリンクを貼っておく。
MeCab: Yet Another Part-of-Speech and Morphological Analyzer

インストール (Windows)

http://sourceforge.net/projects/mecab/files/からmecab本体をダウンロードする。Windowsなら自己解凍インストーラを実行するだけ。Windows版にはIPA辞書が含まれている。Windows 7なら、コントロールパネルで「環境変数」で検索すると、[システムの環境変数の編集]という項目が出てくるので、[システムの環境変数の編集]->[環境変数]でMeCab/bin/にパスを通す。

インストール(Ubuntu)

インストールしたマシンのシステムの情報を一応示しておく。

$ uname -a
Linux vela 2.6.38-8-generic-pae #42-Ubuntu SMP Mon Apr 11 05:17:09 UTC 2011 i686 i686 i386 GNU/Linux

本体のインストール。デフォルトだとJUMAN辞書が含まれているみたい。

$ sudo apt-get install mecab

IPA辞書のほうが精度がよさそうなので、IPAを入れる。

$ sudo apt-get install mecab-ipadic

おそらくデフォルトだと辞書はeuc-jpになっている。utf-8に変えたいときは次のようにする(IPA辞書の場合)。JUMANのほうを変えたいなら、ipadicのところをjumanに変えるだけでいいと思う。

$ sudo /usr/lib/mecab/mecab-dict-index -d /usr/share/mecab/dic/ipadic/ -o /var/lib/mecab/dic/ipadic/ -f euc-jp -t utf-8 -p

辞書の切り替えは次のようにする。

$ sudo update-alternatives --config mecab-dictionary
alternative mecab-dictionary (/var/lib/mecab/dic/debian を提供) には 2 個の選択肢があります。

  選択肢    パス                     優先度  状態
------------------------------------------------------------
* 0            /var/lib/mecab/dic/ipadic   70        自動モード
  1            /var/lib/mecab/dic/ipadic   70        手動モード
  2            /var/lib/mecab/dic/juman    30        手動モード

現在の選択 [*] を保持するには Enter、さもなければ選択肢の番号のキーを押してください:

使い方

コマンドプロンプトmecabと打って、解析したい文を入力するだけ。ちゃんとインストールされていて、パスが通っていたら下のようになるはず。

>mecab
すもももももももものうち
すもも  名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も      助詞,係助詞,*,*,*,*,も,モ,モ
もも    名詞,一般,*,*,*,*,もも,モモ,モモ
も      助詞,係助詞,*,*,*,*,も,モ,モ
もも    名詞,一般,*,*,*,*,もも,モモ,モモ
の      助詞,連体化,*,*,*,*,の,ノ,ノ
うち    名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
EOS

分かち書きしたいときは、-Owakatiオプションをつける。

>mecab -Owakati
飛ばねぇ豚はただの豚だ。
飛ば ねぇ 豚 は ただ の 豚 だ 。

読みだけほしい場合は、-Oyomiオプションをつける

>mecab -Oyomi
「時鳥」ってどう読むの?
「ホトトギス」ッテドウヨムノ?

Nbestがほしい場合は、-Nオプションを使う。下は、読みの3ベストを表示する例.

>mecab -Oyomi -N3
猜疑に歪んだ暗い瞳がせせら笑う。
サイギニユガンダクライヒトミガセセラワラウ。
サイギニヒズンダクライヒトミガセセラワラウ。
サイギニイガンダクライヒトミガセセラワラウ。

Chasen形式で出力したいときは、-Ochasenオプションを使う。

>mecab -Ochasen
はぐれ刑事純情派
はぐれ  ハグレ  はぐれる        動詞-自立       一段    連用形
刑事    ケイジ  刑事    名詞-一般
純情    ジュンジョウ    純情    名詞-一般
派      ハ      派      名詞-接尾-一般
EOS

デフォルトでは未知語に対して適当に品詞推定が行われるが、推定をやめたい場合は-x (--unk-feature)オプションを使う。

>mecab
ロムスカ・パロ・ウル・ラピュタ
ロムスカ・パロ・ウル・ラピュタ  名詞,固有名詞,組織,*,*,*,*
EOS
>mecab -x "未知語"
ロムスカ・パロ・ウル・ラピュタ
ロムスカ・パロ・ウル・ラピュタ  未知語
EOS

引数にファイル名を指定すると、そのファイルを解析対象とする。また、-o (--output)=FILEオプションをつけると出力ファイルを指定できる。

>mecab INPUT -o OUTPUT

ラティス中の全形態素を出力するときは-aオプションを使う。
また、どの程度ラティスを作るかのレベル指定ができる-l0(高速,デフォルト),-l1(Nbestが出せるくらい,中速),-l2(低速)

>mecab -a -l2

g++ (Ubuntu)で使う場合

※ログ取り忘れたので間違っているかもしれないが、libmecab-devを入れとく必要がある。
コンパイルするときは、

% g++ -O2 `mecab-config --cflags` example.cpp -o example `mecab-config --libs`

Visual C++で使う場合

プロジェクトのプロパティで、[C++]->[追加のインクルードディレクトリ]と、[リンカ]->[追加のライブラリディレクトリ]にMeCab/sdk/を加える。そして、[リンカ]->[入力]->[追加の依存ファイル]でMeCab/sdk/libmecab.libを指定する。
※ライブラリのパス指定をするとき、パスにスペースが含まれているときは、ダブルコーテーションでくくる。
※構成(Debug)、(Release)ごとに設定できるようになっているので注意する

コード

ほとんどMeCab: Yet Another Part-of-Speech and Morphological Analyzerのサンプルと同じ。
注意 CのAPIとの違い
mecab_t が MeCab::Tagger
mecab_node_t が MeCab::Node
mecab_new() が MeCab::createTagger() ファクトリ関数
mecab_destroy () はなく,delete を呼んで開放する
※Nbestを出すときはMeCab::createTagger()で-l1(2)オプションを渡しておく

#include <iostream>
#include <mecab.h>

inline void mecab_check(MeCab::Tagger *tagger){
	if( !tagger ) throw MeCab::getTaggerError();
}
template<typename T> inline void mecab_check(T eval, MeCab::Tagger *tagger){
	if( !eval ) throw ( tagger ? tagger->what() : MeCab::getTaggerError() );
}

//サンプルのマクロ
//#define MECAB_CHECK(eval) if (! eval) { \
//   const char *e = tagger ? tagger->what() : MeCab::getTaggerError(); \
//   std::cerr << "Exception:" << e << std::endl; \
//   delete tagger; \
//   return -1; }

int main(int argc, char* argv[]){
	char input[1024] = "流派東方不敗は!王者の風よ!全新系列!天破侠乱!見よ!東方は赤く燃えている!";
	MeCab::Tagger *tagger = MeCab::createTagger(argc, argv);
	const int NBEST = 3;
	try{
		mecab_check(tagger);
		const char *result;
		result = tagger->parse( input );  mecab_check( result, tagger );
		std::cout << "RESULT" << std::endl << result << std::endl;
		
		//Nbest解を得るにはlオプションが必要
		result = tagger->parseNBest( NBEST, input ); mecab_check( result, tagger ); //MECAB_CHECK(result);
		std::cout << "NBEST" << std::endl << result << std::endl; //Nbest分一気に出る

		//解析結果をもっともらしい順に取得する際の初期化
		mecab_check( tagger->parseNBestInit( input ) , tagger);  //tagger->next()でnbestをたどる
		for(int i=0; i< NBEST; i++ ) std::cout << i+1 << " best:" << std::endl << tagger->next() << std::endl;

		//解析結果をMeCab::Node*型で返す
		MeCab::Node* node = (MeCab::Node*)tagger->parseToNode( input ); mecab_check( node, tagger );
		for( ; node; node = node->next ){
			std::cout << "Node ID:" << node->id << ' ';
			switch( node->stat ){
				case MECAB_NOR_NODE: break; //NOR = normal
				case MECAB_BOS_NODE:
					std::cout << "Begin of Sentence "; break;
				case MECAB_EOS_NODE:
					std::cout << "End of Sentence "; break;
				case MECAB_UNK_NODE:
					std::cout << "(Unknown Word)"; //break;
				default:
					//node->surfaceからnode->lengthだけ出すようにしないといけない
					std::cout.write( node->surface, node->length );
			}

			std::cout
				<< ' ' << node->feature                                //素性(CSV形式)
				<< ' ' << (int)(node->surface - input)
				<< ' ' << (int)(node->surface - input + node ->length )
				<< ' ' << node->rcAttr                                 //右コンテキストid
				<< ' ' << node->lcAttr                                 //左コンテキストid
				<< ' ' << node->posid                                  //形態素id
				<< ' ' << (int)node->char_type                         //文字種情報
				<< ' ' << (int)node->stat                              //形態素の種類
				<< ' ' << (int)node->isbest                            //1best ? 0 : 1
				<< ' ' << node->alpha                                  //forward log 確率
				<< ' ' << node->beta                                   //backword log 確率
				<< ' ' << node->prob                                   //周辺確率
				<< ' ' << node->wcost                                  //単語生起コスト
				<< ' ' << node->cost << std::endl;                     //累積コスト
		}
	}catch(const char* str){
		std::cerr << "Exception: " << str <<std::endl; 
		delete tagger;
		return 1;
	}

	return 0;
}