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

ミスの修正とか、品詞を詳しく見るとか

自然言語処理 DBCLS

バイトですよっと。

以前の処理ミスに気づく

pneのrawテキストはこんな感じでxmlもどきっぽい感じになっている。

<キーワード>
タイリングアレイ/chIP-chip/エピゲノミクス/メチル化
</キーワード>

というわけで、中身だけ抜いてくるやつを以前書いていた。

しかし、こんな文章のところでも

2003年にヒトゲノムの塩基配列が決定され^<1)>, 2005年には HapMap プロジェクトによりSNP (一塩基多型) に代表される塩基配列多型が存在することが明らかにされてきた. 

という感じになっていて、「<1)>」のところ以降が読まれなくなったりしていた。なので、以前のスクリプトのルールをもう少し詳細に書くか、、という感じにはなったんだが、「もう全部読み込んでおいて、『キーワード』みたいなやつはSVMとかではじいてもらえばいいんじゃない?」ということになった。

記号、接頭辞、接尾辞etc

今のところ文章をMeCabに投げて、名詞のシークエンスを取ってくるという感じにしているのだけど、id:theclaさんにご指摘していただいていくつかそのままだとまずい例があることが分かった。例えば「抗IL-5レセプター抗体」というようなものがあったとき、こんな感じで形態素解析されてしまう。

/Users/syou6162/dbcls/pne% echo "抗IL-5レセプター抗体" | mecab
抗	接頭詞,名詞接続,*,*,*,*,抗,コウ,コー
IL	名詞,一般,*,*,*,*,*
-	名詞,サ変接続,*,*,*,*,*
5	名詞,数,*,*,*,*,*
レセプター	名詞,一般,*,*,*,*,*
抗体	名詞,一般,*,*,*,*,抗体,コウタイ,コータイ
EOS

これを名詞のシークエンスにしてしまうと「IL-5レセプター抗体」となって「抗」が抜けてしまう。接頭辞の付近を考え忘れていたなあ。

あとはギリシャ文字も「記号」という感じで形態素解析されてしまうので、「20α-水酸化ステロイド脱水素酵素」というのがあったとき

/Users/syou6162/dbcls/pne% echo "20α-水酸化ステロイド脱水素酵素" | mecab  
20	名詞,数,*,*,*,*,*
α	記号,アルファベット,*,*,*,*,α,アルファ,アルファ
-	名詞,サ変接続,*,*,*,*,*
水酸化	名詞,サ変接続,*,*,*,*,水酸化,スイサンカ,スイサンカ
ステロイド	名詞,一般,*,*,*,*,ステロイド,ステロイド,ステロイド
脱	接頭詞,名詞接続,*,*,*,*,脱,ダツ,ダツ
水素	名詞,一般,*,*,*,*,水素,スイソ,スイソ
酵素	名詞,一般,*,*,*,*,酵素,コウソ,コーソ
EOS

という感じでいくつかに分割されてしまうことになる(そしてここにも接頭辞がありますね)。これが原因で「-」で始まる単語とかが結構あったのか。。。

あとは「非ほげほげ」という感じのものや「再ほげほげ」のようなものも接頭辞になってしまうようである。「非ほげほげ」のようなやつも単語として認めるとなると否定系の単語がわんさか入ってくるので入れるかどうか悩ましいんだけど、話し合いの結果とりあえず保留ということにした。

ということで改善したコード。

#include <iostream>
#include <numeric>
#include <string>
#include <set>
#include <map>
#include <vector>
#include <mecab.h>
#include <boost/algorithm/string.hpp>

class MeCabWrapper { // MeCabの解析結果を使いやすい形(vectorのvector)に変換するwrapper用のクラス
public:
  std::vector<std::vector<std::string> > extraction; // 取り出した複合名詞が入っているvectorのvector
  MeCabWrapper(MeCab::Tagger *tagger) {
	this->tagger = tagger;
  };
  virtual ~MeCabWrapper() {
  };
  void addText(std::string text) {
	// 名詞のsequenceならvectorに入れていく
	const MeCab::Node *node = tagger->parseToNode(text.c_str());
	std::string prev_pos = ""; // 前の単語の品詞
	std::vector<std::string> tmp; // 名詞のsequenceをつっこんでおくvector	 

	std::set<std::string> accept_pos_vec; // sequenceとして許容する品詞の集合
	accept_pos_vec.insert("名詞");
	accept_pos_vec.insert("接頭詞");
	accept_pos_vec.insert("記号");

	for( node=node->next; node->next; node=node->next ){ 
	  // 形態素解析されて出てきた文字列(品詞の情報ではなく)
	  char *s = new char [node->length + 1];
	  strncpy(s, node->surface, node->length);
	  s[node->length] = '\0';
	  std::vector<std::string> strvec;
	  boost::algorithm::split(strvec, node->feature, boost::algorithm::is_any_of(","));
	  if (accept_pos_vec.find(strvec[0]) != accept_pos_vec.end()){ // strvec[0]は形態素解析された結果の品詞
		tmp.push_back(s);
	  } else {
		if (accept_pos_vec.find(prev_pos) != accept_pos_vec.end()) { 
		  // 今見ている単語が名詞ではなく、前の品詞が名詞なら名詞のsequenceの終わりになっている
		  extraction.push_back(tmp);
		  tmp.clear();
		}
	  }
	  prev_pos = strvec[0];
	} 	
  }
private:
  MeCab::Tagger *tagger;
};

int main(int argc, char *argv[]) {
  MeCab::Tagger *tagger = MeCab::createTagger("-O wakati");
  MeCabWrapper mw(tagger);

  std::string str = "抗IQGAP1抗体を用いて内在性のIQGAP1の免疫染色 (青色) を行ったところ";
  mw.addText(str);
  for (std::vector<std::vector<std::string> >::iterator vecitr = mw.extraction.begin(); vecitr != mw.extraction.end(); ++vecitr){
	for (std::vector<std::string>::iterator stritr = (*vecitr).begin(); stritr != (*vecitr).end(); ++stritr) {
	  std::cout << (*stritr) << " ";
	}
	std::cout << std::endl;
  }
  delete tagger;  
  return 0;
}

こんな感じになった。

/Users/syou6162/dbcls/src/cpp% g++ -O2 -Wall `mecab-config --cflags` wrapper.cpp `mecab-config --libs` -I/opt/local/include/boost /opt/local/lib/libboost_filesystem-mt-s.a /opt/local/lib/libboost_filesystem-mt.a /opt/local/lib/libboost_system-mt-s.a /opt/local/lib/libboost_system-mt.a
/Users/syou6162/dbcls/src/cpp% ./a.out
抗 IQGAP 1 抗体 
内在 性 
IQGAP 1 
免疫 染色 ( 青色 ) 

よしよし。

.mecabrc

MeCabの辞書を直接いじるのもなんだかなあ、と思っていたらなんか設定ファイルが書けるらしい。

その他

先週、自分が作った専門用語とそれが登場するファイルの転置インデックスをid:theclaさんに渡していたのだけど、それをWebから見れるようになっていました。DBCLSの人は見れるんだけど、まだ公開はされてません(まだ公開できないpdfなどがあるため)。それにより今の蛋白質核酸酵素全文検索に抜けっぽいところがあるようなことも確認できるなどした。

なんとなく成果っぽい感じの手応え*1があって、にやにやするなど。

*1:自分の成果を見える形にしてもらった、という感じか