Hatena::ブログ(Diary)

Shimojimojiの日記

2012-01-28

そういえば 20:24

2011年7月にチームラボを退職して、

会社立ち上げました。

いやまじで。

2010-06-03 FESS

Fess (http://fess.sourceforge.jp/ja/) を触ってみた。

http://fess.sourceforge.jp/ja/articles/article-1.html

を参考にインストールすると、すぐに動作させられた。

FESS は Seesar2 + Solr + S2Robotの検索エンジン

クローラ型の検索エンジンをすぐに構築できる。

DBからのデータのインポートもサポートしている模様。

クロール型のLuceneベースの検索エンジンにnutch

があるが、FessはSeesar2ベースなので検索以外のWebアプリとの親和性も高そう。

ただ、nutchのようなpage rankアルゴリズムをS2Robotがサポートしていない模様?

Webの検索エンジンよりはサイト内検索向きだろうか。

また、日本語処理にはCJKTokenizer(2-gram?)を用いているので

SenやGoSenを使う場合、独自に置き換えてあげる必要がある。

2010-06-01 Solrの検索結果でClustering

Open SourceのClustring EngineにCarrotというものがある。

http://project.carrot2.org/documentation.html

Solr 1.4以降だと簡単にCarrotがいれられるっぽい

http://wiki.apache.org/solr/ClusteringComponent

1. インスコ

/contrib/clustering/で

ant get-libraries

すると/contrib/clustering/lib/downloads/にCarrotに必要なライブラリが落とされる

/contrib/clustering/lib/以下を$TOMCAT_HOME/webapps/solr/WEB-INF/lib/

に突っ込む

さらに

/contrib/clustering/で

ant

してできたapache-solr-clustering-xxxx.jar

も一緒にいれる。



2. 動かしてみる

  • Dsolr.clustering.enabled=true

TomcatJVM引数に設定.

$SOLR_HOME/conf/solrconfig.xmlを設定

<requestHandler name="/clustering"

enable="${solr.clustering.enabled:false}"

class="solr.SearchHandler">

あたりの

<str name="carrot.title">name</str>

<str name="carrot.url">id</str>

<str name="carrot.snippet">feature</str>

を設定

<str name="carrot.snippet">本文のフィールド</str>

に変更!

Tomcat起動

3.Clustering 結果

http://localhost:8080/solr/clustering?q=本文のフィールド:solr&rows=100

で動かしてみた結果。

<lst>
<arr name="labels">
<str>Lucene Solr</str>
</arr>
<arr name="docs">
<str>3</str>
<str>36</str>
<str>37</str>
<str>38</str>
<str>22</str>
<str>10</str>
</arr>
</lst>
<lst>
<arr name="labels">
<str>Solr勉強会</str>
</arr>
<arr name="docs">
<str>4</str>
<str>1</str>
<str>13</str>
<str>14</str>
<str>15</str>
</arr>
</lst>
<lst>
<arr name="labels">
<str>Indexer</str>
</arr>
<arr name="docs">
<str>25</str>
<str>32</str>
<str>7</str>
</arr>
</lst>
<lst>
<arr name="labels">
<str>Magento Solr</str>
</arr>
<arr name="docs">
<str>42</str>
<str>45</str>
<str>40</str>
</arr>
</lst>
<lst>
<arr name="labels">
<str>Senior Hadoop Software Developer with Lucene Solr</str>
</arr>
<arr name="docs">
<str>36</str>
<str>37</str>
<str>38</str>
</arr>
</lst>
<lst>
<arr name="labels">
<str>第3回 Solr勉強会 7/9 金 の告知ページを開けた</str>
</arr>
<arr name="docs">
<str>13</str>
<str>14</str>
<str>15</str>
</arr>
</lst>
</lst>

よさげな結果が返ってきた?

2010-05-31 SolrでFacet検索

Solrには検索結果をClusteringのような機能がある。

シンプルなFacet 02:38

price=10, 20 , .... ,100までの値を持つ

100件のデータ(同じ値を10件もつ)

/solr/select/?q=*:*&facet=true&facet.field=price

した結果は

<lst name="facet_counts">

<lst name="facet_fields">

<lst name="price">

<int name="10.0">10</int>

<int name="20.0">10</int>

<int name="30.0">10</int>

<int name="40.0">10</int>

<int name="50.0">10</int>

<int name="60.0">10</int>

<int name="70.0">10</int>

<int name="80.0">10</int>

<int name="90.0">10</int>

<int name="100.0">10</int>

</lst>

</lst>

</lst>

のようになる。

この結果にprece:10の条件を追加して検索した結果が10件ですよーと表示される。

このように、絞込みのためのナビゲーションが可能になる。

ただ,price(値段)のデータは実際のデータだと

下のようにばらつきのあるデータになるだろう。

10, 20 , .... ,1000までの値を持つ100件のデータに対して同様にリクエストすると

<lst name="facet_counts">

<lst name="facet_fields">

<lst name="price">

<int name="10.0">1</int>

<int name="20.0">1</int>

<int name="30.0">1</int>

<int name="40.0">1</int>

<int name="50.0">1</int>

<int name="60.0">1</int>

<int name="70.0">1</int>

<int name="80.0">1</int>

<int name="90.0">1</int>

<int name="100.0">1</int>

...(略)

</lst>

</lst>

</lst>

このようにまったく固まりになってくれない。。。。

そこでSolrではquery単位でFacetを作る機能がある。

&facet.query=price:[10 TO 100]&facet.query=price:[110 TO *]

クエリを投げると

<lst name="facet_queries">

<int name="price:[10 TO 100]">10</int>

<int name="price:[110 TO *]">90</int>

</lst>

のような結果が返ってくる。

参考:

http://lucene.apache.org/solr/tutorial.html#Faceted+Search

Facetで使えるパラメータ 02:38

参考:http://wiki.apache.org/solr/SimpleFacetParameters

facet.sort

Facet結果をサイズでソートして返す

facet.mincount

サイズの最小値を設定できる。

facet.missing

facet.fieldで指定したフィールドに値を持たない文書の数を返す。

などなど

階層的な絞込みを実現するには 02:38

フィールド1, フィールド2

A, 10

A, 20

....

B, 10

B, 20

....


のようなフィールドを持つとき、

フィールド1がフィールド2の上位の階層であるならば

1階層目の検索は

/solr/select/?q=*:*&facet=true&facet.field=フィールド1

2階層目の検索は

/solr/select/?q=*:*&facet=true&facet.field=フィールド2&fq=フィールド1:フィールド1の値

のようにリクエストすることで

階層的な表示が可能になる。

text フィールドに対するFacet 02:38

日本語文書のフィールドに対して、Facetを作ると助詞や助動詞などの頻度が高く

名詞が埋もれてしまう。

そのため、名詞のみを登録するフィールドを作り、インデクス作成時に名詞のみ(JapaneseAnalyzerのPOSFilterの機能を使う)を登録するなどの対応が必要。

org.apache.lucene.analysys.jaのanalyzer-sen.xml

<accept>

<pos>名詞</pos>

....

</accept>

に登録する品詞情報があるためここから助詞などをはずしてしまう。

また複合語は形態素解析パースされてしまうため、フレーズを登録するなどの対応が必要。

2010-05-30 Solr/Luceneで同義語検索

solrで同義語検索を行う方法。

同義語検索とは 00:19

同義語には以下のようなものがある

表記揺らぎ

twitter <=> ついったー

省略形

United States of America <=> USA

類義語

検索 <=> 探索

ここでは、同義語とは

なんらかのデータソース上(同義語辞書)で定義された2つの語とする。

つまり、辞書上に

(twitter, ついったー)が同義語と定義されていれば同義語とする。


同義語検索とはここでは、

"twitter"で検索した際に、その同義語"ついったー"を含む文書を検索すること。

同義語処理を行うタイミング 00:19

インデクス作成時に行う場合(a)

例えば

"twitterに投稿しました。"

のような文書は

通常時

"twitter に 投稿 し まし た 。"

のようにパースされ。

単語位置情報
twitter1番目の単語
2番目の単語
投稿3番目の単語
する4番目の単語
ます5番目の単語
6番目の単語

のようにインデキシングされるとする。

インデキシングの際に同義語処理を行う場合以下のようなインデクスを作成する。

単語位置情報
twitter1番目の単語
ついったー1番目の単語
2番目の単語
投稿3番目の単語
する4番目の単語
ます5番目の単語
6番目の単語

つまり、n番目の単語の同義語をn番目の位置情報をもつようにインデクスに登録する。

この場合、実際の文書サイズよりもインデクスのサイズが大きくなる。

インデクス作成時に同義語処理を行う場合、

同義語辞書の変更(追加、更新、削除)が反映されるのは、インデクス更新後

であるため、即時反映はできなかったりする。

クエリ受付時に行う場合(b)

"twitter" で 検索された場合, "twitter OR ついったー"で検索する。

より一般的には "w1 op1 w2 op2 w3 ...."に関して

すべてのwnに関してwn を (wn OR wnの同義語)に置換する。

ここで、wnはクエリに出現するn番目の単語で, opnはn番目の演算子(AND, OR, NOTなど)

とする。

複雑なクエリに関して、この処理を行うと検索が重たくなる、デメリットがある。

また、ある単語に関してその同義語を参照する処理が、クエリ毎に発生するため

クエリ処理のパフォーマンスはよろしくない。

インデクス作成時とクエリ受付時に両方で行う場合(c)

インデクス、クエリに同義語関係にある単語のうち片方しか登録しない方法。

つまり、(twitter, ついったー)のうち"ついったー"のみをインデクスする、

例えば"twitterに投稿しました。"という文書は

単語位置情報
ついったー1番目の単語
2番目の単語
投稿3番目の単語
する4番目の単語
ます5番目の単語
6番目の単語

のように片方の表現に寄せた形でインデクスに登録され、

クエリtwitterが存在する場合は"ついったー"に置換する。

こちらの方が(a)の方法よりインデクスサイズは小さく

(b)の方法よりもクエリ時のオーバーヘッドは小さい。

方法(a)の実装 02:59

スキーマ定義

$SOLR_HOME/conf/schema.xml

の/schema/typesに

<fieldType name="text_synonym" class="solr.TextField">

<analyzer type="query" class="org.apache.lucene.analysis.ja.JapaneseAnalyzerGoSen(検索時のアナライザ)"/>

<analyzer type="index" class="org.apache.lucene.analysis.ja.JapaneseSynonymAnalyzer(インデクス時のアナライザ, ここで同義語処理を行う)"/>

</fieldType>

のように記述し、検索時とインデクス作成時に別のアナライザを使うよう設定する。

JapaneseSynonymAnalyzer(日本語形態素解析処理と同義語処理を行うアナライザ)の作成

処理の概要としては

同義語を持つTokenの次に同義語Tokenを返すように変更.

さらに同義語の参照は形態素解析辞書の読みの2番目に登録されているとする。

(注1:すでに複数読みが登録されている場合は消してしまう。というかデフォの読みは全部消しても問題ない)

以下のようなnextメソッドをもつTokenizerを作成

(注2:複数の同義語を使う場合はTokenのListを保持するようにする。)

    private Token synonymToken= null;

    public Token next() throws IOException {
    	
    	if (synonymToken != null){
    		Token resultToken = synonymToken;
    		synonymToken = null;
    		return resultToken;
    	}
    	
        if (!tagger.hasNext()) return null;
        net.java.sen.dictionary.Token token = tagger.next();

        if (token == null) return next();

        //has synonym
        if (hasSynonym(token)){
        	synonymToken = new Token
        	//同義語は読みの2番目に登録される
            (token.getMorpheme().getReadings().get(1),
            	       token.getStart(),
            	       token.end(),
            	       token.getMorpheme().getPartOfSpeech());
        }
      return new Token
      (token.getMorpheme().getBasicForm(),
       token.getStart(),
       token.end(),
       token.getMorpheme().getPartOfSpeech());
    }

そいでGoSenに

twitter,4000,名詞,一般,*,*,*,*,twitter,{twitter/ついったー},{twitter/ついったー}

ついったー,4000,名詞,一般,*,*,*,*,ついったー,{ついったー/twitter},{ついったー/twitter}

こんな感じでデータを作る。

でけた!

f:id:Shimojimoji:20100530025820p:image

方法(c)の実装 00:19

形態素解析辞書の変更

インデクス作成時、クエリ受付時に形態素解析結果の基本形を参照するため、

同義語で基本形を置換するとよい。

たとえば、

表層形基本形品詞
twittertwitter名詞
ついったーついったー名詞

という形態素解析辞書を

表層形基本形品詞
twitterついったー名詞
ついったーついったー名詞

と変更するだけ。

ただしこれだけだと、同義語辞書が変更された場合、元に戻せない問題あるので

辞書更新を

1. 辞書の更新(語の追加、削除)

2. 同義語処理

のように分け、語の追加削除は1の辞書に対して行い

1の結果に対して2の処理を行い、検索には2を利用する。

といった形で

同義語処理前の辞書も残しておく必要がある。

GoSen辞書の更新

GoSenのdictionary.csvの末尾に

twitter,4000,名詞,一般,*,*,*,*,ついったー,ついったー,ついったー

を追加

コンパイル

$GOSEN_HOME/testdata/dictionary/build.xml

<target name="compile" depends="preprocess" unless="dics.complete">

<target name="compile" unless="dics.complete">

に変更して、ダウンロードしたIPADICで上書かれないようにして

antでbuild!

うまくいった。

f:id:Shimojimoji:20100530001918p:image

nowavailablenowavailable 2010/11/23 02:00 ナイスなエントリ、役立ちます。ありがとうございました!

jail-hawkjail-hawk 2010/12/04 12:03 助かりました!!!!ありがとう!Shimojimojiさん!!!