ブログ

News

25/02/2014
LuceneとMahoutの文書分類機能比較

Author: 関口宏司

Lucene は 4.2 から文書分類ツールが提供されるようになりました。そこでこの記事では同じコーパスをLuceneとMahoutで文書分類をして比較してみます。

Lucene には、単純ベイズとk-NN法による分類器が実装されています。次期メジャーリリースのLucene 5に相当するtrunkにはそれに加えて2値分類のパーセプトロンが実装されています。この記事では執筆時の最新バージョンであるLucene 4.6.1を用いて単純ベイズとk-NN法で文書分類をしてみます。

一方、Mahoutは同じく単純ベイズと、さらにランダムフォレストで文書分類を行ってみましょう。

Luceneの文書分類の概要

Luceneの文書分類の分類器は、Classifierインタフェースとして定義されています。

public interface Classifier<T> {

  /**
   * Assign a class (with score) to the given text String
   * @param text a String containing text to be classified
   * @return a {@link ClassificationResult} holding assigned class of type <code>T</code> and score
   * @throws IOException If there is a low-level I/O error.
   */
  public ClassificationResult<T> assignClass(String text) throws IOException;

  /**
   * Train the classifier using the underlying Lucene index
   * @param atomicReader the reader to use to access the Lucene index
   * @param textFieldName the name of the field used to compare documents
   * @param classFieldName the name of the field containing the class assigned to documents
   * @param analyzer the analyzer used to tokenize / filter the unseen text
   * @param query the query to filter which documents use for training
   * @throws IOException If there is a low-level I/O error.
   */
  public void train(AtomicReader atomicReader, String textFieldName, String classFieldName, Analyzer analyzer, Query query)
      throws IOException;
}

Classifierはインデックスを学習データとして用います。そのため、あらかじめ用意したインデックスをオープンしたIndexReaderを用意し、train()メソッドの第1引数に指定します。train()メソッドの第2引数にはトークナイズおよび索引付けされたテキストが入ったLuceneフィールド名を指定します。train()メソッドの第3引数には、文書カテゴリが入ったLuceneフィールド名を指定します。第4引数にはLuceneのAnalyzerを、第5引数にはQueryをそれぞれ渡します。Analyzerは、このあと未知文書を分類する際に使われるAnalyzerを指定します(これはちょっとわかりにくいと私は思います。後述のassignClass()メソッドの引数にむしろするべきだと思いますね)。Queryは、学習に使われる文書を絞り込むのに使われ、その必要がないときはnullを指定します。train()メソッドにはこれ以外に引数を変えた2つのバリエーションがありますが省略します。

Classifierインタフェースのtrain()を呼び出したらString型の未知文書を引数にしてassignClass()メソッドを呼び、分類結果を取得します。ClassifierはJavaのGenericsを用いたインタフェースになっていますが、その型変数Tを用いたClassificationResultクラスがassignClass()の戻り値です。

public class ClassificationResult<T> {

  private final T assignedClass;
  private final double score;

  /**
   * Constructor
   * @param assignedClass the class <code>T</code> assigned by a {@link Classifier}
   * @param score the score for the assignedClass as a <code>double</code>
   */
  public ClassificationResult(T assignedClass, double score) {
    this.assignedClass = assignedClass;
    this.score = score;
  }

  /**
   * retrieve the result class
   * @return a <code>T</code> representing an assigned class
   */
  public T getAssignedClass() {
    return assignedClass;
  }

  /**
   * retrieve the result score
   * @return a <code>double</code> representing a result score
   */
  public double getScore() {
    return score;
  }
}

ClassificationResultのgetAssignedClass()メソッドを呼ぶと、T型の分類結果を得ることができます。

Luceneの分類器のユニークなところは、なんといってもtrain()メソッドではほとんど何も仕事をせず、assignClass()で頑張るところでしょう。これは他の一般的な機械学習ソフトウェアと大きく異なる部分です。一般的な機械学習ソフトウェアの学習フェーズでは、選択した機械学習アルゴリズムに沿ってコーパスを学習してモデルファイルを作成します(この部分に多くの時間を割くわけですが、MahoutはHadoopベースなのでこの部分をMapReduceで時間短縮することを狙っています)。そして分類フェーズでは先に作成したモデルファイルを参照し、未知文書を分類します。一般的にこのフェーズは少量のリソースしか要しません。

Luceneの場合はインデックスをモデルファイルとして使うので、学習フェーズであるtrain()メソッドではほとんど何もする必要がありません(学習はインデックスを作成することで終わっています)。しかし、Luceneのインデックスはキーワード検索を高速に実行するために最適化されており、文書分類のモデルファイルとして適当な形式ではありません。そこで分類フェーズであるassignClass()メソッドでインデックスを検索し、文書分類を行っています。そのため、Luceneの分類器は一般的な機械学習ソフトウェアとは逆に、分類フェーズに大きな計算機パワーを必要とします。しかしながら、検索を主目的に行うサイトではインデックスを作るので、追加投資なしで文書分類ができるLuceneのこの機能は魅力的でしょう。

では次に、Classifierインタフェースの2つの実装クラスがどのように文書分類を行っているか簡単に見ながら、プログラムから実際に呼び出してみましょう。

LuceneのSimpleNaiveBayesClassifierを使う

SimpleNaiveBayesClassifierはClassifierインタフェースの1つめの実装クラスです。名前からわかるとおり、単純ベイズ分類器です。単純ベイズ分類では、ある文書dのときにクラスがcとなる条件付き確率P(c|d)が最大になるcを求めます。このとき、ベイズの定理を用いてP(c|d)を式変形しますが、確率が最大になるときのクラスcを求めるためには、P(c)P(d|c)を求めることになります。通常はアンダーフローを防ぐために対数を計算しますが、SimpleNaiveBayesClassifierのassignClass()メソッドはクラス数の分だけひたすらこの計算を行い、最尤推定を行っています。

では早速、SimpleNaiveBayesClassifierを使ってみたいと思いますが、まずは学習データをインデックスに用意しなければなりません。ここではコーパスとしてlivedoorニュースコーパスを使うことにします。livedoorニュースコーパスを次のようなスキーマ定義のSolrを使ってインデックスに登録します。

<?xml version="1.0" encoding="UTF-8" ?>
<schema name="example" version="1.5">
  <fields>
    <field name="url" type="string" indexed="true" stored="true" required="true" multiValued="false" />
    <field name="cat" type="string" indexed="true" stored="true" required="true" multiValued="false"/>
    <field name="title" type="text_ja" indexed="true" stored="true" multiValued="false"/>
    <field name="body" type="text_ja" indexed="true" stored="true" multiValued="true"/>
    <field name="date" type="date" indexed="true" stored="true"/>
  </fields>
  <uniqueKey>url</uniqueKey>
  <types>
    <fieldType name="string" class="solr.StrField" sortMissingLast="true" />
    <fieldType name="boolean" class="solr.BoolField" sortMissingLast="true"/>
    <fieldType name="int" class="solr.TrieIntField" precisionStep="0" positionIncrementGap="0"/>
    <fieldType name="float" class="solr.TrieFloatField" precisionStep="0" positionIncrementGap="0"/>
    <fieldType name="long" class="solr.TrieLongField" precisionStep="0" positionIncrementGap="0"/>
    <fieldType name="double" class="solr.TrieDoubleField" precisionStep="0" positionIncrementGap="0"/>
    <fieldType name="date" class="solr.TrieDateField" precisionStep="0" positionIncrementGap="0"/>
    <fieldType name="text_ja" class="solr.TextField" positionIncrementGap="100" autoGeneratePhraseQueries="false">
      <analyzer>
        <tokenizer class="solr.JapaneseTokenizerFactory" mode="search"/>
        <filter class="solr.JapaneseBaseFormFilterFactory"/>
        <filter class="solr.JapanesePartOfSpeechStopFilterFactory" tags="lang/stoptags_ja.txt" />
        <filter class="solr.CJKWidthFilterFactory"/>
        <filter class="solr.StopFilterFactory" ignoreCase="true" words="lang/stopwords_ja.txt" />
        <filter class="solr.JapaneseKatakanaStemFilterFactory" minimumLength="4"/>
        <filter class="solr.LowerCaseFilterFactory"/>
      </analyzer>
    </fieldType>
  </types>
</schema>

ここでcatフィールドが分類クラス、bodyフィールドが学習対象フィールドです。上記のschema.xmlを使ってSolrを起動したら、livedoorニュースコーパスを登録します。登録後はSolrを停止してかまいません。

次に、SimpleNaiveBayesClassifierを使うJavaプログラムを用意します。ここでは簡単のために、学習に使う文書をそのまま分類のテストにも使います。プログラムは次のようになります。

public final class TestLuceneIndexClassifier {

  public static final String INDEX = "solr2/collection1/data/index";
  public static final String[] CATEGORIES = {
    "dokujo-tsushin",
    "it-life-hack",
    "kaden-channel",
    "livedoor-homme",
    "movie-enter",
    "peachy",
    "smax",
    "sports-watch",
    "topic-news"
  };
  private static int[][] counts;
  private static Map<String, Integer> catindex;

  public static void main(String[] args) throws Exception {
    init();

    final long startTime = System.currentTimeMillis();
    SimpleNaiveBayesClassifier classifier = new SimpleNaiveBayesClassifier();
    IndexReader reader = DirectoryReader.open(dir());
    AtomicReader ar = SlowCompositeReaderWrapper.wrap(reader);

    classifier.train(ar, "body", "cat", new JapaneseAnalyzer(Version.LUCENE_46));
    final int maxdoc = reader.maxDoc();
    for(int i = 0; i < maxdoc; i++){
      Document doc = ar.document(i);
      String correctAnswer = doc.get("cat");
      final int cai = idx(correctAnswer);
      ClassificationResult<BytesRef> result = classifier.assignClass(doc.get("body"));
      String classified = result.getAssignedClass().utf8ToString();
      final int cli = idx(classified);
      counts[cai][cli]++;
    }
    final long endTime = System.currentTimeMillis();
    final int elapse = (int)(endTime - startTime) / 1000;

    // print results
    int fc = 0, tc = 0;
    for(int i = 0; i < CATEGORIES.length; i++){
      for(int j = 0; j < CATEGORIES.length; j++){
        System.out.printf(" %3d ", counts[i][j]);
        if(i == j){
          tc += counts[i][j];
        }
        else{
          fc += counts[i][j];
        }
      }
      System.out.println();
    }
    float accrate = (float)tc / (float)(tc + fc);
    float errrate = (float)fc / (float)(tc + fc);
    System.out.printf("\n\n*** accuracy rate = %f, error rate = %f; time = %d (sec); %d docs\n", accrate, errrate, elapse, maxdoc);

    reader.close();
  }

  static Directory dir() throws IOException {
    return FSDirectory.open(new File(INDEX));
  }

  static void init(){
    counts = new int[CATEGORIES.length][CATEGORIES.length];
    catindex = new HashMap<String, Integer>();
    for(int i = 0; i < CATEGORIES.length; i++){
      catindex.put(CATEGORIES[i], i);
    }
  }

  static int idx(String cat){
    return catindex.get(cat);
  }
}

AnalyzerにはJapaneseAnalyzerを指定しています(一方、インデックス作成時はSolrの機能を用いてJapaneseTokenizerと関連するTokenFilterを使っており、若干の違いがあります)。文字列配列CATEGORIESには、文書カテゴリがハードコーディングしてあります。このプログラムを実行すると、Mahoutのようなconfusion matrix を表示しますが、matrixの要素はこのハードコーディングされた文書カテゴリの配列要素の順番です。

このプログラムを実行すると、次のようになります。

 760    0    4   23   37   37    2    2    5
  40  656    7   44   25    4   90    1    3
  87   57  392  102   68   24  113    5   16
  40   15    6  391   33    8   16    2    0
  14    2    0    5  845    2    0    1    1
 134    2    2   26  107  549   19    3    0
  43   36   13   17   26   36  693    5    1
   6    0    0   23   35    0    1  829    6
  10    9    9   25   66    6    5   45  595 

*** accuracy rate = 0.775078, error rate = 0.224922; time = 67 (sec); 7367 docs

分類正解率が77%となりました。

LuceneのKNearestNeighborClassifierを使う

Classifierのもうひとつの実装クラスがKNearestNeighborClassifierです。KNearestNeighborClassifierは、コンストラクタの引数に1以上のkを指定してインスタンスを作成します。プログラムはSimpleNaiveBayesClassifierのプログラムとまったく同じものが使えます。SimpleNaiveBayesClassifierのインスタンスを作っている部分をKNearestNeighborClassifierに置き換えるだけです。

KNearestNeighborClassifierもassignClass()メソッドが頑張るのは前述と同じですが、面白いのはLuceneのMoreLikeThisを使っている点です。MoreLikeThisは基準となる文書をクエリとみなして検索を実行するツールです。これにより、基準となる文書と類似した文書を探すことができます。KNearestNeighborClassifierではMoreLikeThisを使ってassignClass()メソッドに渡された未知文書と類似した文書の上位k個を取得します。そしてk個の文書が所属する文書カテゴリの多数決で未知文書の文書カテゴリを決定します。

KNearestNeighborClassifierを使った同じプログラムを実行すると、k=1の場合、次のようになりました。

 724   14   28   22    6   30    8   18   20
 121  630   41   13    2    9   35    6   13
 165   28  582   10    5   16   26    7   25
 229   15   15  213    6   14    6    2   11
 134   37   15    8  603   12   19    7   35
 266   38   39   24   14  412   22    9   18
 810   16    1    3    2    3   32    1    2
 316   18   14   12    5    7    8  439   81
 362   17   29   10    1    7    7   16  321 

*** accuracy rate = 0.536989, error rate = 0.463011; time = 13 (sec); 7367 docs

正解率は53%です。さらにk=3でやってみると、正解率はさらに下がって48%となりました。

 652    5   78    3    7   40   13   38   34
 127  540   82   15    1   10   58   23   14
 169   34  553    3    7   16   38   15   29
 242   10   32  156   12   13   15   10   21
 136   30   21    9  592   11   19   15   37
 309   34   58    5   23  318   40   28   27
 810    8    3    1    0   10   37    1    0
 312    8   44    7    5    2   13  442   67
 362   11   45    5    6   10   16   34  281 

*** accuracy rate = 0.484729, error rate = 0.515271; time = 9 (sec); 7367 docs

NLP4LとMahoutの文書分類

MahoutでLuceneのインデックスを入力データとして扱う場合、便利なコマンドが用意されています。しかし、教師あり学習の文書分類の目的で使う場合は、クラスを示すフィールド情報を文書ベクトルとともに出力する必要があります。

これを簡単に行えるのが弊社開発のNLP4LのMSDDumperやTermsDumperです。NLP4LはNatural Language Processing for Luceneの略であり、Luceneのインデックスをコーパスとみなした自然言語処理ツールセットです。

MSDDumperやTermsDumperは設定によって、指定したLuceneのフィールドからtf*idfなどに基づいた重要語を選択・抽出してMahoutコマンドで読み取りやすい形式で出力してくれます。この機能を利用して、インデックスのbodyフィールドから重要語を2,000語選び、それでMahoutの分類を実行してみます。

結果だけ示すと、Mahoutの単純ベイズでは正解率が96%となりました。

=======================================================
Summary
-------------------------------------------------------
Correctly Classified Instances          :       7128	   96.7689%
Incorrectly Classified Instances        :        238	    3.2311%
Total Classified Instances              :       7366

=======================================================
Confusion Matrix
-------------------------------------------------------
a    	b    	c    	d    	e    	f    	g    	h    	i    	<--Classified as
823  	1    	1    	6    	12   	19   	2    	4    	2    	 |  870   	a     = dokujo-tsushin
1    	848  	2    	1    	0    	1    	11   	4    	2    	 |  870   	b     = it-life-hack
5    	6    	830  	1    	1    	0    	3    	1    	17   	 |  864   	c     = kaden-channel
2    	6    	6    	486  	3    	1    	6    	0    	0    	 |  510   	d     = livedoor-homme
0    	0    	1    	1    	865  	1    	0    	1    	1    	 |  870   	e     = movie-enter
31   	3    	6    	12   	14   	762  	6    	4    	4    	 |  842   	f     = peachy
0    	0    	2    	0    	0    	1    	867  	0    	0    	 |  870   	g     = smax
0    	0    	0    	1    	0    	0    	0    	897  	2    	 |  900   	h     = sports-watch
2    	4    	1    	1    	0    	0    	0    	12   	750  	 |  770   	i     = topic-news

=======================================================
Statistics
-------------------------------------------------------
Kappa                                        0.955
Accuracy                                   96.7689%
Reliability                                87.0076%
Reliability (standard deviation)             0.307

また、Mahoutのランダムフォレストでは正解率が97%となりました。

=======================================================
Summary
-------------------------------------------------------
Correctly Classified Instances          :       7156	   97.1359%
Incorrectly Classified Instances        :        211	    2.8641%
Total Classified Instances              :       7367

=======================================================
Confusion Matrix
-------------------------------------------------------
a    	b    	c    	d    	e    	f    	g    	h    	i    	<--Classified as
838  	5    	2    	6    	3    	7    	2    	0    	1    	 |  864   	a     = kaden-channel
0    	895  	0    	1    	4    	0    	0    	0    	0    	 |  900   	b     = sports-watch
0    	0    	869  	0    	0    	1    	0    	0    	0    	 |  870   	c     = smax
0    	2    	0    	839  	1    	0    	14   	2    	12   	 |  870   	d     = dokujo-tsushin
1    	17   	0    	0    	748  	0    	2    	0    	2    	 |  770   	e     = topic-news
1    	5    	0    	1    	5    	855  	2    	0    	1    	 |  870   	f     = it-life-hack
0    	1    	0    	23   	0    	0    	793  	1    	24   	 |  842   	g     = peachy
0    	11   	0    	14   	1    	2    	18   	454  	11   	 |  511   	h     = livedoor-homme
0    	1    	0    	2    	0    	0    	2    	0    	865  	 |  870   	i     = movie-enter

=======================================================
Statistics
-------------------------------------------------------
Kappa                                       0.9608
Accuracy                                   97.1359%
Reliability                                87.0627%
Reliability (standard deviation)            0.3076

まとめ

この記事では同じコーパスを使って、LuceneとMahoutの文書分類を実行して比較してみました。正解率はMahoutの方が高く見えますが、既に述べた通り、Mahoutの学習データは分類のためにすべての単語を使わず、bodyフィールドの重要語上位2,000語を用いています。一方、正解率が70%にとどまったLuceneの分類器の方は、bodyフィールドのすべての単語を使っています。文書分類用に精査した単語のみを保持するフィールドを設ければ、Luceneでも90%を超える正解率を出せるでしょう。train()メソッドにそのような機能を持つ別のClassifier実装クラスを作る、というのもいいかもしれません。

なお、テストデータを学習に用いないで真の未知データとしてテストすると、80%超程度まで正解率は落ちることを付け加えておきます。

本記事がLuceneとMahoutユーザの皆様のお役に立てれば幸いです。

11/12/2013
Mahoutと機械学習のトレーニングコースを開発しました

Author: 関口宏司

ビッグデータを解析して新しい知見を得ようという取り組みが多くの企業で行われています。そこで不可欠な知識として機械学習が注目されています。

このたびロンウイットでは新しいトレーニングコースApache Mahout ではじめる機械学習を開発しましたので紹介いたします。

「Apache Mahout ではじめる機械学習」のWebページ

Apache Mahout ではじめる機械学習はハンズオン(実習)中心のトレーニングコースです。基本的な機械学習に関する知識が体系立てて整理されており、適宜Mahoutを使う構成になっています。小単元ごとに適切に用意された演習問題を解きながら進められるので、確実に理解が深まり実業務へ応用するための足がかりをつかむことができます。

トレーニングコースの特長

  • 限られた時間内で効率よく学習できるよう、テキストには図表がふんだんに使われています。また、毎ページの詳しいノートは帰社後の独習に大いに役立ちます。
  • 各単元ごとに用意されたやさしい演習問題を講師と一緒に解くことで、背景理論の考え方や実践的知識が確実に身につきます。

演習問題の一例

周長が一定の長方形のうち、面積が最大になるのは正方形であることを、次の2つの方法で示してみましょう。

  • 連立方程式によるシンプルな変数消去による解法
  • ラグランジュの未定乗数法による解法

こんな方におすすめ

  • 今注目の機械学習を体系立てて学び、今後の開発業務に役立てたい方。
  • ビッグデータを保有する企業の情報処理担当者であり、インテグレーターにビッグデータを活用する開発案件を発注するのに必要な最低限の知識を身につけておきたい方。
  • 機械学習系の書籍を購入して勉強したいが、数式が出てきてなかなか読み進められない方。
  • Mahoutイン・アクションを購入してMahoutを使っているが、いまいち使えている気がしない方。

トレーニングコースの内容

トレーニングコースは全2日間となっています。

1日目

初日は「機械学習とは何か」から始まり、パターン認識、教師あり学習のいろいろな分類アルゴリズム、そして最後に手書き文字認識プログラムを作成します。受講者全員参加で手書き文字データを作成します。Mahoutの分類器は果たしてどのくらい手書き文字を認識するでしょうか?お楽しみに!

  • 機械学習とApache Mahout
    • 機械学習とは?
    • モデル [演習]
    • Apache Mahoutとは?
    • Mahoutのインストール [演習]
  • パターン認識
    • パターン認識とは?
    • 特徴ベクトル
    • さまざまな距離測度 [演習]
    • プロトタイプと学習データ
  • 分類
    • 最近傍決定則
    • k-NN法 [演習]
    • 学習によるプロトタイプの決定
    • 識別関数の導出
    • パーセプトロンの学習規則 [演習]
    • 平均化パーセプトロン
    • パーセプトロンの学習規則の問題点
    • Widrow-Hoffの学習規則 [演習]
    • ニューラルネットワーク [演習]
    • サポートベクトルマシン
    • ラグランジュの未定乗数法 [演習]
    • 決定木 [演習]
    • 決定木の学習 [演習]
    • 単純ベイズ [演習]
    • 多変数ベルヌーイモデル [演習]
    • 多クラス分類への拡張
  • 手書き文字認識プログラムを作ろう!
    • 手書き文字認識プログラムの構成
    • 学習データの作成 [演習]
    • 手書き文字認識の実際 [演習]

2日目

2日目はMahoutが提供する分類以外の機能であるレコメンデーション、クラスタリングから始まり、特徴ベクトルの次元削減を目的とした主成分分析、機械学習の評価に関する話、そして最後に自然言語処理における機械学習についてMahoutがどのように使えるか、演習を通じて学んでいきます。

  • レコメンデーション
    • レコメンデーションとは?
    • 情報検索とレコメンデーション
    • レコメンデーションアーキテクチャの種類
    • ユーザプロファイルとその収集
    • 評価値予測 [演習]
    • ピアソンの相関係数
    • レコメンデーションにおける説明
  • ページランク
    • ランキングの重要性
    • 情報検索システムの評価尺度の理論と実際
    • ベクトル空間モデル [演習]
    • Apache Lucene のスコア計算
    • ページランク [演習]
    • HITS
  • クラスタリング
    • クラスタリングとは?
    • クラスタリング手法
    • k平均法 [演習]
    • 最近隣法 [演習]
    • クラスタリング結果の評価と分析
    • 類似画像検索 Apache alike
    • 情報検索とクラスタリング
  • 主成分分析
    • 学習パターン数と次元数の関係 [演習]
    • 主成分分析とは?
    • 平均と分散 [演習]
    • 共分散行列 [演習]
    • 固有値、固有ベクトル [演習]
  • 機械学習の評価
    • 結果の評価と分析
    • 訓練データの分割
    • 精度と再現率 [演習]
    • 偽陽性と偽陰性 [演習]
    • 特徴の評価
    • クラス内分散、クラス間分散
    • ベイズ誤り確率
    • 素性選択
  • 自然言語処理における機械学習
    • 自然言語処理とは?
    • 言語理解のための自然言語処理
    • コーパス
    • bag-of-words
    • Nグラムモデル [演習]
    • 系列ラベリング
    • 隠れマルコフモデル [演習]
    • ビタビアルゴリズム [演習]
    • NLP4Lの紹介

前提知識

演習ではUbuntuマシンを使用しますので、viやEmacsなどのエディタが使えたり、Linuxコマンドを知っているとスムーズに受講できます。

用意するもの

  • sshが使えるノートPCをご用意ください。ノートPCがご用意できない場合はお貸し出しいたします。
  • 手計算による演習問題があるため、鉛筆/シャープペンシル、消しゴムをご用意ください。

お客様のご参加をお待ち申し上げております。

23/10/2012
NLPツール開発にはLucene 4が超便利!

Author: 関口宏司

先日、辞書型コーパスから類義語知識を自動獲得するシステムを開発しました。辞書型コーパスというのは、「見出し語」とその「説明」からなるエントリが多数集まったもので、平たくいえば辞書です。電子辞書や身近なところではWikipediaのデータも辞書型コーパスになります。他にもECサイトにおける「商品名」と「商品説明」も辞書型コーパスとみなすことができます。

このシステムの詳しい説明は、SlideShareをご覧ください。

辞書型コーパスからの類義語知識の自動獲得

このシステムはもともとLucene/SolrのSynonymFilterに使うためのsynonyms.txtをWikipediaから自動的に生成したい、と考えて作成したものです。出力結果のCSVファイルはLucene/SolrのSynonymFilterで使えるのですが、実はこのシステム自身、その内部でLucene 4.0を使っています。

私は従来よりNLPツール開発にはLucene 4.0が便利だろうと感じていて、今回それを実証してみて、あらためてその考えを強くしたところです。

以下、簡単に今回のシステム構築で使用したLucene 4.0のクラスを紹介しましょう。

  • IndexSearcher, TermQuery, TopDocs

    このシステムでは見出し語とその説明から抽出した名詞で構成される類義語候補の類似度を計算します。そして類似度がある閾値より大きいときは、その候補が見出し語の類義語であると判定してCSVファイルに出力します。
    しかし、見出し語とその類義語候補の類似度はどうやったら計算できるでしょう。このシステムでは見出し語の説明文Aaと、類義語候補を使って書かれた辞書エントリの説明文の集合{Ab}の類似度を計算することで近似しています。
    そこで{Ab}を求める必要があるわけですが、このときIndexSearcher, TermQuery, TopDocsなどのクラスを使って類義語候補で説明フィールドを検索し、{Ab}を求めています。
  • PriorityQueue

    次にAaと{Ab}の類似度を計算するのに、それぞれから「特徴語」をピックアップします。そのためにはそれぞれから重要度の大きい単語上位N個を選んで特徴ベクトルを構成します。ここで、重要度には当該単語のTF*IDFを使うこととします(詳しくは前述のSlideShareをご覧ください)。このとき、「重要度の大きい単語上位N個」を選ぶためにPriorityQueueを使っています。
  • DocsEnum, TotalHitCountCollector

    前述の特徴語を抽出する際に重みをTF*IDFで算出したわけですが、このうちTFを求めるのにDocsEnum.freq()を使っています。また、IDFを求めるのに必要なパラメータであるdocFreq(類義語候補を含む記事数)は、IndexSearcherのsearch()メソッドにTotalHitCountCollectorを渡して求めています。
  • Terms, TermsEnum

    類義語候補を見つけるために、「説明」フィールドの中をこれらのクラスを使って探索します。

以上は今回のシステムでのLucene 4.0の使用例ですが、これ以外にもLuceneはNLPツールの開発現場において活躍すると考えています。たとえば、Bootstrapを用いた語彙知識獲得課題では、(1)パターン抽出、(2)パターン選択、(3)インスタンス抽出、(4)インスタンス選択、というサイクルを回すことで、少数シードの教師データから知識を獲得します。このような課題にLuceneを使えば、パターン抽出やインスタンス抽出を簡単な検索問題に置き換えることが可能ではないかと考えています。

27/06/2012
研究室配属

Author: 関口宏司

さてJAISTでの活動ですが、先日自然言語処理の研究室に配属されることが決定しました。授業の方もちょうど「自然言語処理論」が始まり、コーパスをNaive Bayesや決定木やサポートベクターマシンや隠れマルコフモデルなどさまざまな機械学習のアルゴリズムで学習して、分類問題における曖昧性解消を行う方法を学んでいます。

ところで自然言語処理を学んだ学生が卒業後どんなところに就職しているのか気になった私は、研究室のS先生に聞いてみました。すると、「大手電機メーカーのSEとして就職している」ことが多いとのことです。そして、自然言語処理を業務としてしていることはないということでした。もう少し突っ込んで聞いてみると、博士後期課程の学生は卒業後自然言語処理研究を主テーマとする企業の研究所に就職することもあるとのことですが、博士前期課程(修士)卒の学生レベルだと「フツーのSE」をしている、ということでした。どうやらS先生自身も自然言語処理や機械学習という自身の研究室で使われているテクニックが、一部のIT産業界で今ホットであることを全く知らないようでした。

おそらく先生自身のテーマが高難度であり、自然言語を高精度で処理できるのがまだまだ先の話なので、自然言語処理というテーマは研究所の外で活用できるという発想がそもそもないのかもしれません。先生がそういう意識だと、これから社会に出て行こうという学生が自身のテーマ(の基礎知識)が会社で役立つという意識がないままに就職活動をするので、「大手電機メーカーのSEとして就職している」状態になるようです。ここでの先生の表現「大手電機メーカーのSEとして就職している」は、学生からもよく聞かれる質問への定型の回答であり、うちのような研究をしていても卒業後ちゃんとした会社に就職できます、という意味を含んでいます。

もちろん、大手電機メーカーに就職し、フツーのSEをすることが不幸であるといっているわけではありません。しかし、就職後の業務が基幹系システムの開発などだと、周りの環境にも慣らされてしまう中で、研究してきたテーマの基礎知識が自分の開発しているシステム(たとえ基幹系のシステムであっても、テクニックを応用できる部分はゼロではないと思っています!)をより便利にできるという発想自体が生まれにくくなってしまうとするならば、これはアカデミアと産業界双方にとって非常にもったいないことだなあと思った次第です。

13/04/2012
はじめました〜JAIST入学とTwitter

Author: 関口宏司

この4月から大学院生となり、北陸先端科学技術大学院大学の情報科学研究科に入学しました。また、ようやくTwitterもはじめることにしました。 @kojisays で主に学校のことを中心につぶやいてまいります。

社会人向けなので、授業は金曜日の夜と土日に行われます。先週の土曜日に新入生のオリエンテーションがあり、その翌日日曜日の朝から授業があり、さらに早速レポートまで出て想像以上に大変です。

土曜日の履修科目は統計的信号処理特論(Statistical Signal Processing)というもので、確率過程(Stochastic Process)に関する基本的な数学的取り扱いと、統計を考慮した信号処理アルゴリズムとモデル推定の方法について習得することを目的としています。初日は確率過程まで話が進まず、確率論の基本で終わってしまいましたが、現在Lucene 4.0で導入されたBM25やLanguage Modelなどの確率モデル系のスコア計算を調べているので、ちょうどいいタイミングの授業となりました。

soleamiのクエリログの解析でも、本講の知識が応用できたりしないかなあと夢想しています。たとえば、あるキーワードが一定時間内に検索される回数を確率過程としてとらえるとか。そうすると、将来どのくらい検索されるか数学的に予測できる、なんていうのはどうでしょうか。

soleamiではこういったアイディアをどんどん取り入れ、楽しいものにしていくので、今後ともよろしくお願いします。

17/02/2012
アップロードしていただくログファイルについてご注意

soleamiを公開して2日経ち、徐々にユーザ登録とファイルのアップロードのカウントが増えております。「クエリログを見るのって面白い!」と認識を新たにしていただけた人が一人でも多くなれば、soleamiを公開した者として大変うれしく思います。

ところでsoleamiでSolrのクエリログを可視化していただくためには、クエリログのフォーマットに少々厳しい制限が、現在残念ながらあります。

制限についてはこちらにまとめてありますのでご覧ください。これらのフォーマットにしたがっていないログファイルは、たとえ「分析完了」のメール通知がいったとしても、可視化の画面では”no log data…”と表示されてしまいます。たとえば、これまで以下のようなログファイルをアップロードしていただいていますが、残念ながら可視化できません。

  • 日付のフォーマットが英語以外の月名になっている。
    14 f?vr. 2012 06:39:23
    

    残念ながら、日付部分のフォーマットは、次のフォーマットのみが認識できます。

    Jan 14, 2012 9:21:27 AM
    2012/1/14 09:21:27
    

    これ以外のフォーマットは現在認識することができません。

  • 1クエリが1行のフォーマットになっている。現在はSolrのデフォルトであるJULを使った1クエリが2行で記録されるフォーマットしか分析できません。

soleami開発チームでは今後も改善を続け、これらの制限が緩和できるようにしたいと考えています。

今後ともご愛顧のほど、よろしくお願いします!

14/02/2012
soleami (ソレミ)の使い方〜はじめてのSolrの立ち上げからログの可視化まで〜

Apache Solrが出力するクエリログを可視化するツールsoleamiの使い方をご紹介します。本稿ではもっとも一般的な使い方を想定し、SolrをTomcat上にデプロイして使う場合について解説します。

おおまかな手順は、以下の通りです(すでにTomcat上で稼働しているSolrをお持ちの方は、手順4.からお読みください)。

  1. Tomcatのダウンロード
  2. Solrのダウンロード
  3. 文字化け対策
  4. solrconfig.xmlの編集
  5. SolrのTomcatへのデプロイと起動
  6. 検索を実行
  7. soleamiでログを可視化

なお、Linux/Macの環境を前提に話を進めます。Windowsの場合はcygwinなどを使いながら、置き換えて読み進めてください。

またユーザsolrがログインディレクトリ/home/solrの下にworkというディレクトリを作成し、その下に環境を作成して作業するものとします。そこでまず、workというディレクトリを作成しておきます。

$ pwd
/home/solr
$ mkdir work
$ cd work
$ pwd
/home/solr/work

なお、JavaとAntがあらかじめインストールされていることが必要です。

Tomcatのダウンロード

まず、Tomcat 6.0.35をダウンロードします。

# workディレクトリにTomcat 6.0.35をダウンロード
$ pwd
/home/solr/work
$ wget http://ftp.meisei-u.ac.jp/mirror/apache/dist/tomcat/tomcat-6/v6.0.35/bin/apache-tomcat-6.0.35.tar.gz

そしてダウンロードしたファイルを解凍します。

$ wget tar xvzf apache-tomcat-6.0.35.tar.gz
x apache-tomcat-6.0.35/bin/catalina.sh
x apache-tomcat-6.0.35/bin/digest.sh
x apache-tomcat-6.0.35/bin/setclasspath.sh
x apache-tomcat-6.0.35/bin/shutdown.sh
x apache-tomcat-6.0.35/bin/startup.sh
x apache-tomcat-6.0.35/bin/tool-wrapper.sh
x apache-tomcat-6.0.35/bin/version.sh
     :

Tomcatに関しての準備は以上です。

Solrのダウンロード

次にSolrをダウンロードします。ここでは本日時点の最新バージョンである3.5.0をダウンロードします。注意していただきたいのは、後述するように「文字化け対策」をする必要があるので、Solrのバイナリ版ではなく、ソース版をダウンロードする必要があることです。

# workディレクトリにソース版Solr 3.5.0をダウンロード
$ pwd
/home/solr/work
$ wget http://ftp.jaist.ac.jp/pub/apache//lucene/solr/3.5.0/apache-solr-3.5.0-src.tgz

そしてダウンロードしたファイルを解凍します。

$ tar xvzf apache-solr-3.5.0-src.tgz
x apache-solr-3.5.0/dev-tools/
x apache-solr-3.5.0/dev-tools/eclipse/
x apache-solr-3.5.0/dev-tools/idea/
x apache-solr-3.5.0/dev-tools/idea/.idea/
x apache-solr-3.5.0/dev-tools/idea/.idea/copyright/
x apache-solr-3.5.0/dev-tools/idea/.idea/libraries/
x apache-solr-3.5.0/dev-tools/idea/lucene/
x apache-solr-3.5.0/dev-tools/idea/lucene/contrib/
     :

以上でSolrの準備自体は終わりですが、次に「文字化け対策」の作業が必要です。

文字化け対策

ダウンロードしたままのTomcatにSolrをデプロイすると、確実に文字化けが発生してしまいます。そこでTomcatとSolrに対して以下の作業が必要になります。

Tomcatのserver.xmlの編集

Tomcatのserver.xmlをエディタで開きます。

$ vi apache-tomcat-6.0.35/conf/server.xml

そして次のConnector要素の部分を見つけ、useBodyEncodingForURI=”true”を追記します。

    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" useBodyEncodingForURI="true"/>

できたら、server.xmlファイルを保存してエディタを終了します。

Tomcat提供のサーブレットフィルターをSolrにコピー

次に、TomcatのexamplesにあるサーブレットフィルターをSolrにコピーします。

$ cp -R apache-tomcat-6.0.35/webapps/examples/WEB-INF/classes/filters apache-solr-3.5.0/solr/core/src/java

Solrのweb.xmlを編集

次にSolrのweb.xmlを編集します。適当なエディタでweb.xmlを開いてください。

$ vi apache-solr-3.5.0/solr/webapp/web/WEB-INF/web.xml

そしてweb.xmlファイルの中の既存のfilter定義の前に次のようなSetCharacterEncodingFilterを使用する旨の記述を挿入します。

  <filter>
    <filter-name>SetCharacterEncoding</filter-name>
    <filter-class>filters.SetCharacterEncodingFilter</filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
  </filter>
  <!-- 以下は既存の定義です -->
  <filter>
    <filter-name>SolrRequestFilter</filter-name>
     :

次に同じファイルのもう少し下を見ていくと、filter-mappingを定義している箇所があります。同じくその前に次のようにSetCharacterEncodingの定義を挿入します。

  <filter-mapping>
    <filter-name>SetCharacterEncoding</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  <!-- 以下は既存の定義です -->
  <filter-mapping>
    <filter-name>SolrRequestFilter</filter-name>
      :

Solrのビルド

以上でビルドの準備が整いましたので、antを使ってSolrをビルドします。

$ cd apache-solr-3.5.0/solr
$ pwd
/home/solr/work/apache-solr-3.5.0/solr
$ ant -Dversion=3.5.0 example

solrconfig.xmlの編集

次に、solrconfig.xmlから、(ログの可視化には)不要な部分を取り除きます。この手順は必須ではありませんが、デフォルトの状態だとSolr起動時におかしなクエリが飛んでしまうので、可視化した際にゴミが目立ってしまいます。そこで、次のように適当なエディタでsolrconfig.xmlファイルを開きます。

$ pwd
/home/solr/work/apache-solr-3.5.0/solr
$ vi example/solr/conf/solrconfig.xml

そして以下のようなfirstSearcherの箇所を見つけ、listenerタグごと削除するか、以下のようにコメントアウトしてください。

    <listener event="firstSearcher" class="solr.QuerySenderListener">
      <arr name="queries">
<!-- ここの部分をコメントアウトします。
        <lst>
          <str name="q">static firstSearcher warming in solrconfig.xml</str>
        </lst>
-->
      </arr>
    </listener>

SolrのTomcatへのデプロイと起動

次に、SolrのwarファイルをTomcatにデプロイします。前述の手順でSolrをビルドすると、Solrのdistというディレクトリの下にwarファイルが作られています。このファイルはapache-solr-3.5.0.warという名前になっているので、solr.warと名前を変えて、Tomcatのwebappsディレクトリの下にコピーしてデプロイします。

$ pwd
/home/solr/work
$ cp apache-solr-3.5.0/solr/dist/apache-solr-3.5.0.war apache-tomcat-6.0.35/webapps/solr.war

そしてTomcatを起動します。その際、環境変数CATALINA_OPTSにSolrホームディレクトリ(solrconfig.xmlファイルが配置されているディレクトリの親ディレクトリ)を指し示すように設定が必要です。また環境によっては、システムプロパティfile.encodingの設定も必要になりますので、環境に合わせてTomcatを起動してください。

$ export CATALINA_OPTS="-Dsolr.solr.home=/home/solr/work/apache-solr-3.5.0/solr/example/solr -Dfile.encoding=UTF-8"; ./apache-tomcat-6.0.35/bin/startup.sh

検索を実行

ではブラウザからTomcatにアクセスして管理画面を立ち上げてみましょう。ブラウザから http://localhost:8080/solr/admin/ にアクセスします。

soleami-howto-solr-admin

Solrの管理画面

ここでは、街中検索系のサイトを運営していると仮定して、管理画面の入力エリアに「渋谷 ラーメン」と入力してSearchボタンをクリックします。なお、「渋谷」と「ラーメン」の間にはスペースが入っています。いうまでもなく、渋谷でラーメン屋さんを探しているようなイメージです。

すると、XMLで検索結果が戻ってきますが、データも入れていないので当然0件の検索結果になります。1回だけのクエリではつまらないのでもう一つ、「渋谷 郵便局」という検索もやっておきましょう。ブラウザの戻るボタンで管理画面に戻り、入力エリアに「渋谷 郵便局」と入力してSearchボタンをクリックします。

この状態で、Tomcatのログを確認しておきましょう。catalina.outの最後の方に今検索した「渋谷 ラーメン」や「渋谷 郵便局」という行が記録されているはずです。

$ tail apache-tomcat-6.0.35/logs/catalina.out
2012/02/14 14:02:46 org.apache.solr.core.SolrCore execute
情報: [] webapp=/solr path=/select/ params={indent=on&start=0&q=渋谷+ラーメン&version=2.2&rows=10} hits=0 status=0 QTime=37
2012/02/14 14:02:58 org.apache.solr.core.SolrCore execute
情報: [] webapp=/solr path=/select/ params={indent=on&start=0&q=渋谷+郵便局&version=2.2&rows=10} hits=0 status=0 QTime=1

このとき、文字化けしていたり、文字化けしていなくてもUTF-8で記録されていない場合は、あとで可視化できなくなってしまいますので、その場合はこれまでの手順を見直してください。

soleamiでログを可視化

さていよいよcatalina.outのクエリログを可視化してみましょう。もしまだsoleamiにユーザ登録していなかったら、ここで ユーザ登録 から登録しておいてください。

ではsoleamiのメニューのアップロードをクリックします。ここで未ログインの場合はログインを促されますので、登録したユーザ名(メールアドレス)とパスワードを使ってログインします。

すると下のようなログファイルをアップロードする画面が表示されます。

soleami-howto-upload

soleamiのファイルアップロード画面

画面に指示のあるとおり、ログファイルはZIP圧縮してアップロードしてください。その際に、機密保持のためにシステムから指定されたパスワードで暗号化する必要があります(実は暗号化しなくても動作しますが、後述するようにアップロード中はSSL通信しないので、暗号化するようにしてください)。

$ cd apache-tomcat-6.0.35/logs
$ zip -e catalina.out.zip catalina.out
Enter password: fRciL_yU
Verify password: fRciL_yU
  adding: catalina.out (deflated 92%)

ここではZIP圧縮するファイルはcatalina.outひとつだけですが、ディレクトリに複数のクエリログファイルがある場合、そのディレクトリごとZIP圧縮しても問題ありません。また、各ログファイルは.logという拡張子を持っていなくても大丈夫です。ただし、まとめてZIP圧縮する場合は、余計な(ゴミ)ファイルは含まれないようにしてください。

なお、ZIP圧縮後のファイルサイズは10MBを超えてはいけません(10MBを超える場合は、複数回に分けてアップロードしてください。最大過去12ヶ月分のログが可視化できます)。

ZIP圧縮したらファイルアップロードの画面でZIPファイルを選択し、[アップロード] ボタンをクリックします。すると、お使いのブラウザによっては、次のような「セキュリティ警告」が表示されます。

soleami-howto-security-warning

ファイルアップロードしようとすると、セキュリティ警告が表示される

これは、今表示されている「ファイルアップロード画面」自体がSSLで送信されているのに対し、アップロードしようとしているZIPファイルがSSLを使っていないためです。しかし、ZIPファイルは暗号化されているので機密は保たれていますから、[続ける] をクリックしてアップロードします。

アップロード終了後、数分〜数十分待つと次のようなメールがsoleamiから届きます。

おめでとうございます!

アップロードしていただきましたログファイルの解析が終了しました。
下記リンクをクリックし、可視化を体験してください!

http://soleami.com/p/visualize?locale=ja

アップロード時刻(日本時間): 2012/02/14 14:44:47
データベースを初期化する: No

ではメール中にあるリンクをクリックして、「可視化」を開始してみましょう。

「可視化」の画面は最初は[トップ10] タブが選択された状態になっています。今回は「渋谷 ラーメン」および「渋谷 郵便局」という検索を行いましたので、下の画面のように今月の「トップ10」が表示されます(なお、先月以前の部分は「no log data…」と表示されます)。

soleami-howto-top10

トップ10チャートのサンプル

次に[トレンド1000] のタブを選択してみましょう。すると、次のように検索キーワードのリストが表示されます。

soleami-howto-trend1000-list

トレンド1000の検索キーワードリストの表示

ここで、「渋谷(2)」をクリックします。すると、次のような折れ線グラフと棒グラフが表示されます。

soleami-howto-trend1000-chart

トレンド1000で「渋谷」を選択したところ

ここで折れ線グラフの方は「渋谷」という検索キーワードの過去12ヶ月間の検索回数の推移を表しています。そして棒グラフの方は、主キーワードである「渋谷」と一緒に検索された副キーワードの過去12ヶ月間の累積回数を表しています。これらのチャートから「渋谷」で「ラーメン」屋や「郵便局」を探している人がいるんだなあ、ということが読み取れます。

次に[0件ヒット] のタブを選択してみましょう。すると先ほどと同じようにクエリのリストが表示されます。違うのは、先ほどは検索キーワードの単位にリスト表示されていたのが、ここではクエリ文字列単位になっていることです。「渋谷 ラーメン(1)」「渋谷 郵便局(1)」となっていますので、「渋谷 ラーメン(1)」をクリックしてみます。すると次の画面のようなチャートが表示され、「0件ヒット」を起こした「渋谷 ラーメン」というクエリの過去12ヶ月間の推移がわかります。

soleami-howto-zerohits

0件ヒットで適当なクエリを選択してチャートを表示したところ

「0件ヒット」は検索システムがユーザ(サイト訪問者)のニーズに応えられなかったことを表しています。特にECサイトでは「0件ヒット」の発生は重大視される傾向にあります。「0件ヒット」が起こった場合は、類義語辞書を使っているサイトではその内容を見直したり、コンテンツを検討するなどして発生を少なくすることが、ユーザの満足度向上につながります。

今回のサンプルではたった2回の検索実行を可視化しただけでしたが、Solrの起動から可視化までの流れをつかんでいただけたことと思います。実際にSolrを運用しているログファイルを分析すると、季節による検索キーワードの傾向が読み取れたり、また企業内検索では「社員が今求めているもの」を把握することができます。

Solrとともにsoleamiを上手に活用していただき、貴社の検索システムのコンテンツの充実化や利便性向上にぜひお役立てください!

14/02/2012
[プレスリリース] Apache Solrのクエリログ可視化サービス

株式会社ロンウイット(本社:東京都千代田区、代表取締役社長:関口宏司)はオープンソース・ソフトウェアの検索エンジンApache Solr(アパッチ ソーラー)のクエリログを可視化するサービスsoleami(ソレミ)を開始しました。利用料金は無料です。

soleami
http://soleami.com/ja

Solrはインターネットで公開されているWebサイトのサイト内検索や、非公開のイントラネットの企業内検索など、世界中で広く使われているOSSの検索エンジンです。

Solrは実行した検索リクエストをログファイル(クエリログ)に記録しています。クエリログは「サイト訪問者のニーズのリスト」とも言えるものですが、検索キーワードごとに集計する作業は大変な労力がかかるため、これまで活用されてきていませんでした。

soleamiはこの問題を解決するため、クエリログをアップロードするだけで自動的に集計を行い、Webブラウザでチャートを表示できることを目標に開発されました。Solrの管理者は誰でも無料で、Solrのクエリログをsoleamiにアップロードし、サイト訪問者のニーズを可視化できるようになりました。

soleamiが作成するチャートには次の種類があります(画面サンプルはこちら)。

  • トップ10
    毎月のトップ10の人気検索キーワードを集計して表示します。過去12ヶ月間までさかのぼって表示できるので、検索キーワードの季節変動も読み取れます。
  • トレンド1000
    上位1,000位までの検索キーワードの過去12ヶ月間の傾向が折れ線グラフで表示されます。さらにその検索キーワードと一緒に検索された副キーワードのトップ20が棒グラフで表示されます。
  • 0件ヒット
    「0件ヒット」を起こしたクエリ(検索結果を表示できなかったクエリ)の過去12ヶ月間の検索回数が棒グラフで表示されます。「0件ヒット」の発生はサイト訪問者の検索ニーズに応えられなかったことを示します。0件ヒットを減らすことは、サイト訪問者の満足度を高め、ECサイトではコンバージョン率の向上に寄与します。

soleami開発チームは今後も開発を継続し、クエリログをさまざまな切り口から視覚化できるように機能を向上させていきたいと考えています。


soleamiの名前の由来

soleamiは“ami du soleil”(太陽の友人)というフランス語からの造語です。solarと一字違いのSolrにはもともと太陽の意味がありました。soleamiをSolrの友達のように常にそばに置いて使ってもらいたいという願いが込められています。


Apache Solrについて

オープンソース・ソフトウェアを開発・管理する非営利団体Apache Software Foundationが開発しているOSSの検索エンジンです。SolrはHadoopの創始者であるDoug Cutting氏が開発したApache Luceneをベースにしています。

http://lucene.apache.org/solr/


株式会社ロンウイットについて

Apache Lucene/Solrを企業・学校等に導入する支援事業を展開しています。Solrの導入時コンサルティングの他、Solrの教育サービスやサポートサービスなども提供しています。代表の関口はApache Lucene/Solrのコミッターも務めています。

http://www.rondhuit.com/

14/02/2012
soleami – Apache Solrのクエリログ可視化サービスの開始!

Apache Solrのクエリログをさまざまな切り口から分析してチャート表示するサービス”soleami”を本日より一般公開いたします。

soleami – サイト訪問者のニーズを可視化
http://soleami.com/ja

Solrは実行した検索リクエストをログファイル(クエリログ)に記録しています。クエリログは「サイト訪問者のニーズのリスト」とも言えるものですが、検索キーワードごとに集計する作業は大変な労力がかかるため、これまで活用されてきていませんでした。

soleamiはこの問題を解決するため、クエリログをアップロードするだけで自動的に集計を行い、Webブラウザでチャートを表示できることを目標に開発されました。Solrの管理者は誰でも無料で、Solrのクエリログをsoleamiにアップロードし、サイト訪問者のニーズを可視化できるようになりました。

クエリログの可視化に必要なのは次のたったの3ステップ。とっても簡単です。

  1. Solrが出力するクエリログファイルを保存
  2. クエリログファイルをZIP圧縮してsoleamiにアップロード
  3. あとは見るだけ!

soleamiが作成するチャートには次の種類があります(画面サンプルはこちら)。

  • トップ10
    毎月のトップ10の人気検索キーワードを集計して表示します。過去12ヶ月間までさかのぼって表示できるので、検索キーワードの季節変動も読み取れます。
  • トレンド1000
    上位1,000位までの検索キーワードの過去12ヶ月間の傾向が折れ線グラフで表示されます。さらにその検索キーワードと一緒に検索された副キーワードのトップ20が棒グラフで表示されます。
  • 0件ヒット
    「0件ヒット」を起こしたクエリ(検索結果を表示できなかったクエリ)の過去12ヶ月間の検索回数が棒グラフで表示されます。「0件ヒット」の発生はサイト訪問者の検索ニーズに応えられなかったことを示します。0件ヒットを減らすことは、サイト訪問者の満足度を高め、ECサイトではコンバージョン率の向上に寄与することにもつながります。

現在チャートの種類はこれだけですが、soleami開発チームは今後も開発を継続し、クエリログをさまざまな切り口から視覚化できるように機能を向上させていきたいと考えています。「こんな切り口で分析してみたい」というご要望があれば、ぜひメールでお知らせください

soleamiをSolrとともにご活用いただき、あなたのWebサイトの利便性向上にお役立てください。