関口宏司のLuceneブログ

OSS検索ライブラリのLuceneおよびそのサブプロジェクト(Solr/Tika/Mahoutなど)について
<< Luke 0.7.1のリリース | main | 人名がヒットしたときはスコアを上げる >>
記事から名詞だけを取り出す
Lucene本の6章では、TermFreqVectorを使って、ブッシュ大統領の就任演説や小泉純一郎首相(当時)の所信表明演説の単語の出現頻度分析を行うプログラムを紹介した。そのプログラムでは「ごみ」を取り除くために「3文字以下のひらがな」を一律に切り捨てていた。

今回は、Lucene 2.2でサポートされたペイロード機能を使って同様のことをもう少しエレガントにやってみたい。

ペイロードは、インデックスに登録する単語ごとに記録できるバイトストリームのことである。登録するときは、Tokenに対してsetPayload()メソッドを呼び出す。そのときにPayloadのオブジェクトを引数に渡せばよい。

ペイロードはTokenにセットするので、TokenizerやTokenFilterで設定するのがよいだろう。そこでSenTokenizerを変更して、形態素解析器Senが出力する品詞情報をペイロードに設定することにする。そして単語出現頻度分析時に名詞だけを出力するようにプログラミングする。

SenPayloadTokenizerクラス

まずTokenにSenの品詞情報をペイロードとしてセットするSenPayloadTokenizerを作成する。SenPayloadTokenizerはSenTokenizerを拡張して実装する。SenTokenizerのほとんどの部分を再利用できるので、プログラムは次のようにとても簡単なものとなる:



public class SenPayloadTokenizer extends SenTokenizer {
public SenPayloadTokenizer(Reader in, String configFile) throws IOException {
super( in, configFile );
}
public Token next() throws IOException {
Token token = input.next();
if( token == null ) return null;
String pos = token.type().trim();
token.setPayload( new Payload( pos.getBytes() ) );
return token;
}
}



名詞だけを取り出すプログラム

記録したPayloadはTermPositionsから取り出せるようになっている。TermPositionsインタフェースのPayload関連のメソッドだけを示すと、次のとおりである:



public interface TermPositions {
public byte[] getPayload( byte[] data, int offset );
public int getPayloadLength();
public boolean isPayloadAvailable();
}



各メソッドの働きはメソッド名称そのままなので特に説明は必要ないだろう。

TermPositionsを使うには、インデックスにどのようなTermがあるかをあらかじめ知っておく必要がある。ここではセオリーどおり、TermEnumを使ってインデックス内の全Termを調べる。次のプログラムはTermEnumで全Termを調べるようにループし、TermPositionsからPayloadを取得してTermとPayload(品詞情報)を表示するプログラムである:



IndexReader reader = IndexReader.open( dir );
TermEnum te = reader.terms( new Term( F, "" ) );
while( te.next() ){
Term t = te.term();
if( !t.field().equals( F ) ) break;
TermPositions tps = reader.termPositions( t );
tps.next();
tps.nextPosition();
if( tps.isPayloadAvailable() ){
int len = tps.getPayloadLength();
byte[] payload = new byte[len];
tps.getPayload( payload, 0 );
int freq = tps.freq();
String pos = new String( payload );
System.out.println( t.text() + "¥t" + freq + "¥t" + pos );
}
tps.close();
}
te.close();
reader.close();



今回のプログラムの目標は名詞だけを取り出すので、Payloadが名詞かどうかを判定する次のようなメソッドを用意する:



private static final byte[] POS = "名詞".getBytes();

private static boolean isNoun( byte[] payload ){
int p = 0;
for( byte b : POS )
if( payload[p++] != b ) return false;
return true;
}



今回はサンプルなので、品詞情報はSenが出力する品詞情報文字列をそのままPayloadに登録しているが、本稼動のプログラムでは符号化ルールを定めて品詞情報文字列ではなく、1バイトのPayloadに収まるようにするのがよいだろう。そうでないとそれこそ品詞情報がインデックスのお荷物(荷重;Payload)となってしまう。

プログラムは出現頻度の高い上位10位の名詞のリストを表示するようにするためComparatorなどの実装がさらに必要となり、最終的なプログラムは次のようになる:



public class ExtractNounTerms {

private static final String CONTENT = "XXXXXXXXXX";

private static final String F = "f";
private static final int LIST_MAX = 10;
private static final byte[] POS = "名詞".getBytes();
private static Directory dir;
private static Analyzer analyzer = new JapaneseAnalyzer();

public static void main(String[] args) throws IOException {
dir = new RAMDirectory();
makeIndex();
List list = new ArrayList();
extractNounTerms( list );
printList( list );
}

private static void makeIndex() throws IOException{
IndexWriter writer = new IndexWriter( dir, analyzer );
writer.setMaxFieldLength( Integer.MAX_VALUE );
Document doc = new Document();
doc.add( new Field( F, CONTENT, Store.NO, Index.TOKENIZED ) );
writer.addDocument( doc );
writer.close();
}

private static void extractNounTerms( List list ) throws IOException{
IndexReader reader = IndexReader.open( dir );
TermEnum te = reader.terms( new Term( F, "" ) );
while( te.next() ){
Term t = te.term();
if( !t.field().equals( F ) ) break;
TermPositions tps = reader.termPositions( t );
tps.next();
tps.nextPosition();
if( tps.isPayloadAvailable() ){
int len = tps.getPayloadLength();
byte[] payload = new byte[len];
tps.getPayload( payload, 0 );
if( isNoun( payload ) ){
int freq = tps.freq();
String pos = new String( payload );
list.add( new TermData( t.text(), freq, pos ) );
}
}
tps.close();
}
te.close();
reader.close();
}

private static boolean isNoun( byte[] payload ){
int p = 0;
for( byte b : POS )
if( payload[p++] != b ) return false;
return true;
}

private static void printList( List list ){
Collections.sort( list, new TermDataComparator() );
for( int i = 0; i < LIST_MAX && i < list.size(); i++ ){
TermData td = list.get( i );
System.out.println( td.term + "¥t" + td.freq + "¥t" + td.pos );
}
}

static class TermData {
String term;
int freq;
String pos;
TermData( String term, int freq, String pos ){
this.term = term;
this.freq = freq;
this.pos = pos;
}
}

static class TermDataComparator implements Comparator {
public int compare(TermData o1, TermData o2) {
return o1.freq == o2.freq ? 0 : ( o1.freq > o2.freq ? -1: 1 );
}
}
}



上記のプログラムのXXXXXXXXXXで示した場所に適当な文章(長文)をセットし、コンパイルして実行してみる。

実行に当たっては、JapaneseAnalyzerがSenPayloadTokenizerを使用するように、XML設定ファイルを用意し、システムプロパティを
指定して実行する(詳しくはLucene本P.40〜41参照)。

次は、XXXXXXXXXXの部分に安倍首相の所信表明演説を貼り付けて実行したときの出力結果である:



的 36 名詞-接尾-形容動詞語幹
国 32 名詞-一般
日本 28 名詞-固有名詞-地域-国
こと 27 名詞-非自立-一般
化 23 名詞-接尾-サ変接続
社会 21 名詞-一般
国民 17 名詞-一般
地方 16 名詞-一般
改革 16 名詞-サ変接続
私 14 名詞-代名詞-一般



小泉首相のときは「郵政」「民営」「化」というのがはっきり現れてそれなりに面白かったが、安倍さんの場合は名詞だけを取り出しているせいか例の「美しい国」が見えてこないのでつまらない(「国」だけは出現回数32回あるが・・・)。

そこでためしにプログラムの「名詞」の部分を「形容詞」に変えて実行してみる。すると、期待通り(!)「美しい」を連呼していることがわかった:



美しい 9 形容詞-自立
ふさわしい 4 形容詞-自立
厳しい 4 形容詞-自立
新しい 4 形容詞-自立
若い 4 形容詞-自立
高い 3 形容詞-自立
やすい 2 形容詞-自立
長い 2 形容詞-自立
おいしい 1 形容詞-自立
ほしい 1 形容詞-非自立



楽しくなってきたので、さらに、取り出す品詞を「名詞-固有名詞-地域-国」に変えてやってみると次のようになった:



日本 28 名詞-固有名詞-地域-国
日 6 名詞-固有名詞-地域-国
米 5 名詞-固有名詞-地域-国
北朝鮮 4 名詞-固有名詞-地域-国
イラク 2 名詞-固有名詞-地域-国
米国 2 名詞-固有名詞-地域-国
インド 1 名詞-固有名詞-地域-国
オーストラリア 1 名詞-固有名詞-地域-国
ジャパン 1 名詞-固有名詞-地域-国
ロシア 1 名詞-固有名詞-地域-国



「日」や「米」は果たして本当に国なのかは怪しいがなかなか面白い(中国には言及していないんですね)。

きりがないのでこの辺で止めるが、Lucene 2.2のペイロードを使ってインデックスにSenが出力する品詞を埋め込むと、いろいろ面白そうなことができることがわかった。
| 関口宏司 | Lucene自由自在 | 19:00 | comments(4) | trackbacks(0) |
こんにちは、はじめまして。 日本でSolrを使用したイーコマースサイトの例を教えて頂けますか? どこまで優れているのかテストしたいのですが。 よろしくお願いいたします。
| りんご | 2007/08/30 10:09 AM |
はじめまして。

残念ながら日本でSolrのイーコマースでの例は私は知りません。イーコマースでなければいくつかありますが、事例としてご説明できるもの、名前を伏せる必要があるものなどありますので、ブログでは申し上げられません。

よろしければ弊社お申し込みフォームにご連絡ください。情報交換をさせていただきたいと思います。

http://www.rondhuit.com/inquiry_form.html
| 関口 | 2007/08/30 10:49 AM |
了解いたしました。
| りんご | 2007/08/31 8:51 AM |
http://www.katespadepurses.us.com/ kate spade laptop bag
http://www.fitflops-sale.us.com/ fitflop sandals
http://www.hermesbirkin-handbags.us.com/ hermes bag
http://www.pandorajewelryscharms.us.com/ pandora bracelet
http://www.adidas-ultraboost.us.com/ adidas boost
http://www.curry4-shoes.us.com/ curry shoes
http://www.nikeairmax-90.us.com/ air max 90
http://www.pandora-jewelrysale.us.com/ pandora
http://www.pandorajewelryrings.us.com/ pandora jewelry
http://www.lebron15-shoes.us.com/ lebron 14
http://www.nikeairvapormaxflyknit.us.com/ nike air vapormax flyknit
http://www.airmax90shoes.us.com/ nike air max 2018
http://www.jordans11shoes.us.com/ jordan retro 12
http://www.adidasyeezy-350.us.com/ yeezy boost 350
http://www.pumafentyrihannashoes.us.com/ puma slides
http://www.louboutinredbottoms.us.com/ red bottoms sneakers
http://www.fitflopsshoes.us.com/ fitflops clearance
http://www.jordan11spacejams.us.com/ jordan 13
http://www.salomonspeedcross3.us.com/ salomon shoes
http://www.birkenstocksandalssale.us.com/ birkenstock shoes
http://www.adidasnmdrunnerr1.us.com/ yeezy boost 750
http://www.yeezyboost350shoes.us.com/ yeezy 950
http://www.kd10-shoes.us.com/ nike kd
http://www.longchampbag.us.com/ longchamp
http://www.kyrie-4.us.com/ nike kyrie
ドリフトliuyuzhen
| addidas nmd | 2018/03/02 7:34 PM |









http://lucene.jugem.jp/trackback/133
+ Solrによるブログ内検索
+ PROFILE
  12345
6789101112
13141516171819
20212223242526
2728293031  
<< May 2018 >>
+ LINKS
検索エンジン製品 - 比較のポイント
商用検索エンジンを購入した企業担当者は読まないでください。ショックを受けますから・・・
>>製品比較 10のポイント
+ Lucene&Solrデモ
+ ThinkIT記事
+ RECOMMEND
Apache Solr入門 ―オープンソース全文検索エンジン
Apache Solr入門 ―オープンソース全文検索エンジン (JUGEMレビュー »)
関口 宏司,三部 靖夫,武田 光平,中野 猛,大谷 純
+ RECOMMEND
Lucene in Action
Lucene in Action (JUGEMレビュー »)
Erik Hatcher,Otis Gospodnetic,Mike McCandless
FastVectorHighlighterについて解説記事を寄稿しました。
+ RECOMMEND
+ SELECTED ENTRIES
+ RECENT COMMENTS
+ RECENT TRACKBACK
+ CATEGORIES
+ ARCHIVES
+ MOBILE
qrcode
+ SPONSORED LINKS