スキップしてメイン コンテンツに移動

Search::Fulltext で N-gram 検索できるように Search::Fulltext::Tokenizer::Ngram を書いた

2014-01-01: CPAN にアップロードしたので追記。

要旨

Search::Fulltext という大変シンプルな全文検索モジュールがリリースされていたので、N-gram トークナイザを提供する Search::Fulltext::Tokenizer::Ngram を書きました。

これを使うと日本語として怪しい表現でもとりあえずヒットするような全文検索ができます。

動機

大変シンプルなモジュールだったのでシンプルに使ってみようと思ったら現在のところ日本語のトークナイザは Search::Fulltext::Tokenizer::MeCab のみでした。Text::MeCab のインストールが億劫なのと、ワンダー日本語が跋扈している Web 文書なんかの検索だと N-gram の方が都合が良いこともあるので作ってみました。

念の為: N-gram って何

テキストを N 文字毎に区切ったもの。e.g., "色彩を持たない多崎つくると、彼の巡礼の年" から 2-gram を作ると "色彩", "彩を", "を持", ... "の年" といった具合になる。 要は文書中に出現する N 文字の組合せを網羅するので、N-gram を使ってインデックスを作ると N 文字以上のクエリにヒットする文書は一切取り零さなくなる。 短所は形態素の区切りを知らないので "京都" というクエリで "東京都" という語を含んだ文書までヒットする、N 文字以下のクエリは一切ヒットしない、N が小さいとインデックスが大きくなるなど。

使い方

インストール

cpanm などを使ってインストールできます:
cpanm Search::Fulltext::Tokenizer::Ngram
Dist::Zilla (dzil) が必要です。面倒なら CPAN に上がるのを待つか `lib/` 以下をコピーで動きます。
git clone git@github.com:sekia/Search-Fulltext-Tokenizer-Ngram.git    
cd Search-Fulltext-Tokenizer-Ngram
dzil test
dzil install

使用例

1-gram, 2-gram, 3-gram が使えます。

use strict;
use warnings;
use utf8;
use Search::Fulltext;
use Search::Fulltext::Tokenizer::Unigram;  # 1-gram tokenizer
use Search::Fulltext::Tokenizer::Bigram;   # 2-gram tokenizer
use Search::Fulltext::Tokenizer::Trigram;  # 3-gram tokenizer

my $search_engine = Search::Fulltext->new(
  docs => [
    'ハンプティ・ダンプティ 塀の上',
    'ハンプティ・ダンプティ 落っこちた',
    '王様の馬みんなと 王様の家来みんなでも',
    'ハンプティを元に 戻せなかった',
  ],
  # 3-gram を使う
  tokenizer => q/perl 'Search::Fulltext::Tokenizer::Trigram::create_token_iterator_generator'/,
);

# search はヒットした文書のインデックスを返す。ここでは [0, 1, 3]。
my $hit_documents1 = $search_engine->search('ハンプティ');

# ヒットしない。
# インデックスが 3-gram で構築されているため2文字の "王様" は載っていない。
my $hit_documents2 = $search_engine->search('王様')

4文字以上の N-gram が必要なら Search::Fulltext::Tokenizer::Ngram を継承して作ることができます:

package MyTokenizer::42gram {
  use parent qw/Search::Fulltext::Tokenizer::Ngram/;

  sub create_token_iterator_generator {
    sub { __PACKAGE__->new(42)->create_token_iterator(@_) };
  }
}
my $search_engine = Search::Fulltext->new(
   docs => [ ... ],
   tokenizer => q/perl 'MyTokenizer::42gram::create_token_iterator_generator'/,
);

TODO

  • Documentation.
  • Upload to CPAN.

まとめ

大変シンプルなモジュールを大変シンプルに使うことができるようになりました。Enjoy!

参考文献

コメント

  1. Search::Fulltextの作者です.
    Search::Fulltext::Tokenizer::* をどなたかが作ってくださるのを期待していたので,大変嬉しく思っております.

    よろしければ,Search::Fulltext::Tokenizer::NgramをCPANにアップロードなさいませんか?
    大まかな手順としてはこちらのサイトが参考になります.
    http://blog.livedoor.jp/sasata299/archives/51284970.html

    Search::Fulltext::Tokenizer::* に期待されるREADME(Pod)の書き方などはこちらをご覧いただければと思います.
    https://github.com/laysakura/Search-Fulltext-Tokenizer-MeCab

    楽な作業とは言えませんが,是非ご検討くださいませ.

    返信削除

コメントを投稿

このブログの人気の投稿

Perl 5 to 6 - コンテキスト

2011-02-27: コメント欄で既に改訂された仕様の指摘がありました ので一部補足しました。 id:uasi に感謝します。 これはMoritz Lenz氏のWebサイト Perlgeek.de で公開されているブログ記事 "Perl 5 to 6" Lesson 06 - Contexts の日本語訳です。 原文は Creative Commons Attribution 3.0 Germany に基づいて公開されています。 本エントリには Creative Commons Attribution 3.0 Unported を適用します。 Original text: Copyright© 2008-2010 Moritz Lenz Japanese translation: Copyright© 2011 SATOH Koichi NAME "Perl 5 to 6" Lesson 06 - コンテキスト SYNOPSIS my @a = <a b c> my $x = @a; say $x[2]; # c say (~2).WHAT # Str() say +@a; # 3 if @a < 10 { say "short array"; } DESCRIPTION 次のように書いたとき、 $x = @a Perl5では $x は @a より少ない情報—— @a の要素数だけ——しか持ちません。 すべての情報を保存しておくためには明示的にリファレンスを取る必要があります: $x = \@a Perl6ではこれらは反対になります: デフォルトでは何も失うことなく、スカラ変数は配列を単に格納します。 これは一般要素コンテキスト(Perl5で scalar と呼ばれていたもの)及びより特化された数値、整数、文字列コンテキストの導入によって可能となりました。無効コンテキストとリストコンテキストは変更されていません。 特別な構文でコンテキストを強制できます。 構文 コンテキスト ~stuff 文字列 ?stuff 真理値 +stuff ...

LIBLINEAR 2.41 で One-class SVM が使えるようになったので Perl から触ってみよう

改訂 (Sep 15, 2020): 必要のない手順を含んでいたのでサンプルコードと記述を修正しました。 CPAN に Algorithm::LibLinear 0.22 がリリースされました (しました。) 高速な線形 SVM およびロジスティック回帰による複数の機械学習アルゴリズムを実装したライブラリである LIBLINEAR への Perl バインディングです。 利用している LIBLINEAR のバージョンが LIBLINEAR 2.30 から LIBLINEAR 2.41 に上がったことで新しいソルバが追加され、One-class SVM (OC-SVM) による一値分類が利用可能になっています (しました。) OC-SVM って何 一値分類を SVM でやること。 一値分類って何 ある値が学習したクラスに含まれるか否かを決定する問題。 HBO の「シリコンバレー」に出てきた「ホットドッグ」と「ホットドッグ以外」を識別するアプリが典型。「ホットドッグ以外」の方は犬でも神でも一つの指輪でも何でも含まれるのがミソ。 二値分類の場合正反両者のデータを集める必要があるのに対して、一値分類の学習器は正例データのみしか要求しない (ものが多い。) 主な用途は外れ値検出で、もちろんホットドッグやホットドッグ様のものを検出したりもできる。 使い方 手順自体は他の二値ないし多値分類問題と同じです。つまり、 訓練パラメータを決めて 訓練データセットで訓練して テストデータセットで確度を検証して 十分良くなったらモデルを保存する といういつもの流れ。 訓練パラメータ use 5.032 ; use Algorithm::LibLinear ; my $learner = Algorithm::LibLinear ->new( epsilon => 0.01 , nu => 0.75 , solver => ' ONECLASS_SVM ' , ); solver => 'ONECLASS_SVM' が一値分類用のソルバです。LIBLINEAR の train コマンドで言うところの -s 21 。 OC-SVM の良いところは (ハイパー)...

Perl 5 to 6 - ツイジル

これはMoritz Lenz氏のWebサイト Perlgeek.de で公開されているブログ記事 "Perl 5 to 6" Lesson 15 - Twigils の日本語訳です。 原文は Creative Commons Attribution 3.0 Germany に基づいて公開されています。 本エントリには Creative Commons Attribution 3.0 Unported を適用します。 Original text: Copyright© 2008-2010 Moritz Lenz Japanese translation: Copyright© 2011 SATOH Koichi NAME "Perl 5 to 6" Lesson 15 - ツイジル SYNOPSIS class Foo { has $.bar; has $!baz; } my @stuff = sort { $^b[1] <=> $^a[1]}, [1, 2], [0, 3], [4, 8]; my $block = { say "This is the named 'foo' parameter: $:foo" }; $block(:foo<bar>); say "This is file $?FILE on line $?LINE" say "A CGI script" if %*ENV.exists('DOCUMENT_ROOT'); DESCRIPTION いくつかの変数にはツイジルという第2のシジルがあります。これは基本的にはその変数が「普通」ではないということです。違いはいくつかあり、例えばスコープの違いなどです。 オブジェクトのパブリックな属性とプライベートな属性がそれぞれ . と ! というツイジルを持つことは既に紹介しました; それらは通常の変数ではなく self に結びつけられています。 ツイジル ^ はPerl5で例外的に扱われていたケースを一般化します。次のように書けます # 注意: Perl5のコードです sort ...

多分週刊チラシの裏 (Sep 28 - Oct 04, 2020)

Chrome Web Store が有料 Chrome 拡張の取扱を終了 Chrome Web Store で提供されている有料 Chrome 拡張及びアプリ内課金 API の両方が 2021 年 1 月いっぱいで廃止される。 開発者はそれまでに代替となるサードパーティの課金 API に移行し、購入済ライセンスの移行手段も用意する必要がある。 この決定の発表時点で新規の有料ないしアプリ内課金のある Chrome 拡張の新規登録は終了している。実際のところ 2020 年 3 月時点で既に「一時的に」停止されており、その措置が恒久化されただけとの由。 シェルスクリプティングには長いオプションを使え 「短いオプション (e.g., -x ) はコマンドライン上での略記である。スクリプトにおいては自分や将来の同僚のためにも長いオプション (e.g., ---do-something ) を与える方が理解が容易だろう」という主張。 異論の余地なく正論である。 CobWeb - COBOL to WebAssembly Compiler COBOL から WebAssembly へのコンパイラ。いやマジで。 Cloudflare が何を思ったか同社のサーバレス環境である Workers に COBOL 対応を追加した際 の成果物である。 COBOL から C へのトランスレータである GNU COBOL と C コードをコンパイルして WebAssembly を出力する Emscripten から成っており、他の言語に比べて軽量なバイナリを生成するとのこと。 「ウチではそんな風にはやらないんだ (“We don’t do that here”)」 昨今ソフトウェア開発のコミュニティでも Code of Conduct を用意するところが増えてきたが、コミュニティの文化を明文化するのは難しい。 長大な「べからず集」は息苦しいし、肯定的なガイドラインは時に抽象的で実効的に使えない。問題となるようなふるまいの動機が善意であった場合は特にそうだ。 仮に優れたガイドラインがあっても、それに基いて人を実際に咎めるのは骨が折れることである。初中やればコミュニティ内でも疎まれる。 話の分かる相手ならそれでもまだ説得する意義もあるが、Web 上の対話で当事者双方が納得し合っ...

Perl 5 to 6 - 遅延性

これはMoritz Lenz氏のWebサイト Perlgeek.de で公開されているブログ記事 "Perl 5 to 6" Lesson 12 - Laziness の日本語訳です。 原文は Creative Commons Attribution 3.0 Germany に基づいて公開されています。 本エントリには Creative Commons Attribution 3.0 Unported を適用します。 Original text: Copyright© 2008-2010 Moritz Lenz Japanese translation: Copyright© 2011 SATOH Koichi NAME "Perl 5 to 6" Lesson 12 - 遅延性 SYNOPSIS my @integers = 0..*; for @integers -> $i { say $i; last if $i % 17 == 0; } my @even := map { 2 * $_ }, 0..*; my @stuff := gather { for 0 .. Inf { take 2 ** $_; } } DESCRIPTION Perlプログラマは怠けがちです。彼らが使うリストも。 ここで怠惰という言葉が意味するのは、評価が可能な限り遅延されるということです。 @a := map BLOCK, @b のようなコードを書いたとき、ブロックは一切実行されません。 @a の要素にアクセスしようとしたときだけ map は実際にブロックを実行し、必要とされる分だけ @a を埋めます。 代入ではなくバインディングを使っていることに注意して下さい: 配列への代入は先行評価を強制することがあります(コンパイラがリストの無限性に気づかない限り; 無限リスト検出の詳細はまだ固まっていません)。 バインディングはそのようなことがありません。 遅延性は無限リストの取り扱いを可能にします: 引数すべてに操作を行うようなことさえしなければ、評価された要素に必要なだけのメモリしか必要としません。 しかし落とし穴があります: 長さの...