pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

66
ゆるいテキスト検索 ふたたび N-gram編 ぬこ@横浜(@nuko_yokohama)

Upload: toshi-harada

Post on 22-Jun-2015

1.340 views

Category:

Technology


9 download

DESCRIPTION

2013/7/13 PostgreSQL Unconference

TRANSCRIPT

Page 1: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

ゆるいテキスト検索ふたたび

N-gram編ぬこ@横浜(@nuko_yokohama)

Page 2: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

自己紹介名前:ぬこ@横浜

仕事:ラーメンレビュー副業:某通信系SI会社勤務

(趣味で)誰得なPostgreSQL拡張やってます

Page 3: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

つくったものxml_fdw:XMLファイルのFDW

w24:二十四節気型ksj:漢数字で演算する型

あいまいtextsearch用パッチneo4j_fdw:グラフデータベースのFDW

redis_fdw 9.3対応(やりかけ)

誰得?

Page 4: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

今回のテーマpg_bigm+近似検索

Page 5: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

pg_bigmって?

Page 6: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

PostgreSQL組込み用N-gram全文検索系

pg_trgnは3文字分割pg_bigmは2文字分割

Page 7: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

NTT DATAの藤井さんと澤田さんが作っている。

公開中。(”pg_bigm”で検索)

Page 8: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

pg_bigmの売り

日本語をサポート2文字以下の検索が速い

Page 9: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

詳しいことは(たぶん今日来ている)藤井さんと澤田さんに

聞いてください。

Page 10: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

さて、前回のアンカンファレンス

発表でこんなことを言った

Page 11: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

「N-gramで近似検索は無理」と言ったな。

あれは嘘だ。

Page 12: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

確かにN-gramだけでは無理

Page 13: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

たとえば・・・

メロスは激怒した。セリヌンティウスも激怒した。「俺の名前はセンヌリティウス

じゃねー!」

Page 14: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

確かにN-gramだけでは無理っす

meros=# SELECT data FROM test WHERE data LIKE likequery('セリヌンティウス') LIMIT 1; data ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ メロスには竹馬の友があった。セリヌンティウスである。今は此のシラクスの市で、石工をしている。その友を、これから訪ねてみるつもりなのだ。久しく逢わなかったのだから、訪ねて行くのが楽しみである。(1 row)

meros=# SELECT data FROM test WHERE data LIKE likequery('センヌリティウス') LIMIT 1; data ------(0 rows)

ですよねー

Page 15: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

しかし

Page 16: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

一手間かかるが近似検索ぽいことは

実はできる。

Page 17: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

そのためには公式リリース版のpg_bigmには機能が足りない・・・

pg_trgmにはある・・・

Page 18: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

なので作者さんに丁重にお願いしてsimilarity関数等を

作ってGithubに上げてもらった

pg_trgmでもいいんだけどさ・・・せっかくだからpg_bigmの試験も兼ねて

澤田さん、サンクス!

Page 19: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

similarity関数

文字列間の類似度を算出(0.0~1.0)して返却

1.0なら完全一致0.0なら全く一致しない

Page 20: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

% 演算子

%:類似度が閾値以上なら真閾値のデフォルトは0.3

pg_bigm.similarity_limit パラメータで変更可能

Page 21: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

こんな感じbigm=# SHOW pg_bigm.similarity_limit ; pg_bigm.similarity_limit -------------------------- 0.3(1 row)

bigm=# SELECT similarity('センヌリティウス', 'セリヌンティウス'); similarity ------------ 0.384615(1 row)

bigm=# SELECT 'センヌリティウス' % 'セリヌンティウス'; ?column? ---------- t(1 row)

bigm=# SET pg_bigm.similarity_limit = 0.4;SETbigm=# SELECT 'センヌリティウス' % 'セリヌンティウス'; ?column? ---------- f(1 row)

Page 22: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

N-gram近似検索のやりかた

Page 23: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

テキストから辞書をMecab等で生成。

文書も辞書もN-gramインデクスを設定。

あいまいな語と辞書を比較し、近似した語を取得。取得した語で近似検索。

Page 24: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

辞書テーブル

・・・

シラクス

セリヌンティウス

ゼウス

・・・ 文書テーブル

メロスは激怒した・・・

きょう未明メロスは村・・・

・・・

「セリヌンティウス。・・・

・・・

 勇者は、ひどく赤面・・・

セリヌンティウス

センヌリティウス類似検索で

一番類似した「セリヌンティウス」

を取り出す

「セリヌンティウス」を使ってLIKE検索で文書テーブルを

検索する。

だいたいこんな感じ

文書から抜き出したキーワード

汎用辞書

Page 25: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

やってみた

Page 26: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

確かにN-gramだけでは無理っす

SELECT data FROM meros WHERE data LIKE likequery( (SELECT data FROM token WHERE data % 'センヌリティウス' ORDER BY similarity(data, 'センヌリティウス') DESC) ) LIMIT 3; data ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- メロスには竹馬の友があった。セリヌンティウスである。今は此のシラクスの市で、石工をしている。その友を、これから訪ねてみるつもりなのだ。久しく逢わなかったのだから、訪ねて行くのが楽しみである。 「そうです。帰って来るのです。」メロスは必死で言い張った。「私は約束を守ります。私を、三日間だけ許して下さい。妹が、私の帰りを待っているのだ。そんなに私を信じられないならば、よろしい、この市にセリヌンティウスという石工がいます。私の無二の友人だ。あれを、人質としてここに置いて行こう。私が逃げしまって、三日目の日暮まで、ここに帰って来なかったら、あの友人を絞め殺して下さい。たのむ、そうして下さい。」  メロスは腕に唸《うな》りをつけてセリヌンティウスの頬を殴った。(3 rows)

やったね☆

Page 27: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

確かにN-gramだけでは無理っす

EXPLAINSELECT data FROM meros WHERE data LIKE likequery( (SELECT data FROM token WHERE data % 'センヌリティウス' ORDER BY similarity(data, 'センヌリティウス') DESC) ) LIMIT 3; QUERY PLAN -------------------------------------------------------------------------------------------- Limit (cost=56.04..60.06 rows=1 width=32) InitPlan 1 (returns $0) -> Sort (cost=44.03..44.04 rows=1 width=32) Sort Key: (similarity(token.data, 'センヌリティウス'::text)) -> Bitmap Heap Scan on token (cost=40.01..44.02 rows=1 width=32) Recheck Cond: (data % 'センヌリティウス'::text) -> Bitmap Index Scan on token_data_idx (cost=0.00..40.01 rows=1 width=0) Index Cond: (data % 'センヌリティウス'::text) -> Bitmap Heap Scan on meros (cost=12.01..16.02 rows=1 width=32) Recheck Cond: (data ~~ likequery($0)) -> Bitmap Index Scan on meros_data_idx (cost=0.00..12.01 rows=1 width=0) Index Cond: (data ~~ likequery($0))(12 rows)

インデクスもきちんと使われる

Page 28: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

pg_trgm+近似検索簡単かつ

それなりに有効?

Page 29: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

しかし、この方式はまだまだ欠陥がある

Page 30: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

「インタフェース」問題ふたたび

Page 31: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

インタフェースインタフェイス

インターフェースインターフェイス

私はこの表記派

Page 32: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

参考:各表記の類似度インタフェース インタフェイス インターフェース インターフェイス

インタフェース 1.0 0.6 0.7 0.416667

インタフェイス 0.6 1.0 0.416667 0.7

インターフェース 0.7 0.416667 1.0 0.636364

インターフェイス 0.416667 0.7 0.636364 1.0

Page 33: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

ということは、Similarityで閾値以上の類語をOR条件で繋げば

表記ゆれ検索もある程度カバーできる?

Page 34: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

でも、さっきのクエリをそのまま使うとエラーに

なる・・・

Page 35: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

エラー回避のために一番安直なのは“LIMIT 1”

を指定して1行のみ返却

Page 36: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

こんな感じ

SELECT data FROM test WHERE data LIKE likequery( (SELECT token FROM token WHERE token % 'インタフェース' ORDER BY similarity(token, 'インタフェース') DESC) );ERROR: more than one row returned by a subquery used as an expression

SELECT data FROM test WHERE data LIKE likequery( (SELECT token FROM token WHERE token % 'インタフェース' ORDER BY similarity(token, 'インタフェース') DESC LIMIT 1) )

Page 37: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

しかし、それでは複数の候補に展開できない

Page 38: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

どうやって複数の候補を条件に展開するか

Page 39: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

Point

% 演算子行から配列を生成

サブクエリText配列へのcast

ANY演算子

Page 40: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

こんな感じSELECT data FROM test WHERE data LIKE ANY ( (SELECT array_agg(sml.token) FROM (SELECT likequery(token) AS token FROM token WHERE token % 'インタフェース' ORDER BY similarity(token, 'インタフェース') DESC LIMIT 5) as sml)::text[]);

%演算子で辞書を検索一番近いN件をORDER Byで取得その結果をarray_aggで配列化

そのサブクエリの結果をtext[]でキャスト配列をLIKE ANYで評価

Page 41: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

こんな感じSELECT data FROM test WHERE data LIKE ANY ( (SELECT array_agg(sml.token) FROM (SELECT likequery(token) AS token FROM token WHERE token % 'インタフェース' ORDER BY similarity(token, 'インタフェース') DESC LIMIT 5) as sml)::text[]); data --------------------------------------------------------------------------- 今月号のインターフェースはコンパイラ特集だ。 実装はユーザインタフェースだけでなく、ユーザエクスペリエンスを考えねばならない。 発注元との意識ずれでインタフェイスの再設計をすることになった。 僕の考えた最強のユーザー・インターフェイスは却下された。(4 rows)

やったね☆

Page 42: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

「インタフェース」で「インターフェース」「インタフェイス」

「インターフェイス」がヒットした。

Page 43: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

べたにSQLを書くのはちょっと大変・・・

ラッパ関数を作ろう。

Page 44: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

こんな感じ

CREATE OR REPLACE FUNCTION create_synonyms(keyword text, limit_num int) RETURNS text[] AS $$ SELECT array_append(array_agg(sml.token), likequery(keyword)) FROM (SELECT likequery(token) AS token FROM token WHERE token % keyword ORDER BY similarity(token, keyword)  DESC LIMIT limit_num) as sml; $$ LANGUAGE sql;

Page 45: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

ラッパ関数を使うとちょっとだけ

シンプルに記述できる

Page 46: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

こんな感じ

SELECT data FROM test WHERE data LIKE ANY ((SELECT create_synonyms('インタフェース', 5))::text[]); data ------------------------------------------------------------------------ 今月号のインターフェースはコンパイラ特集だ。 実装はユーザインタフェースだけでなく、ユーザエクスペリエンスを考えねばならない。 発注元との意識ずれでインタフェイスの再設計をすることになった。 僕の考えた最強のユーザー・インターフェイスは却下された。

やったね☆

Page 47: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

あのさぁ・・・(棒読み)

色々やってるけどそれくらいなら

LIKE '%インタ%ス%'で出来るんじゃないの?

Page 48: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

そうかもしれないけど・・・

でも、メタ文字を書かせたら負けな気がする。

それに・・・

Page 49: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

「カレーライス」で「ライスカレー」を検索できるのか?

これはLIKEの書き方では無理なはず。

Page 50: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

こんな感じ

SELECT data FROM test WHERE data LIKE ANY ((SELECT create_synonyms('ライスカレー'))::text[]); data ------------------------------------------------------------ 彼女は言った。「カレーライスが嫌いな男子なんていません!」 昨晩食べたライスカレーはとても美味かった。(2 rows)

Page 51: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

更なる拡張

Page 52: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

Mecab辞書(ipadic)にはありがたいことに

読みがなもついている。

Page 53: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

あいまい語↓

類義語 or 読みがな

これも展開するとさらにゆるく検索できないか?

Page 54: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

読みがな側も類似検索すれば、かなのtypoにも

対応できるかも。

Page 55: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

やってみた

Page 56: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

こんな感じSELECT * FROM test WHERE data LIKE ANY ((SELECT create_synonyms2('小田原市'))::text[]); data -------------------------------------------------------------- 神奈川の西部には小田原ラーメンというカテゴリの醤油ラーメンがある。 源平の戦いの英雄の一人、那須与一の郷は栃木の大田原市である。 私はコーディングスタイルには然程こだわらないようにしている。 僕は小市民の星を目指している。面倒事は勘弁してくれ。(4 rows)

(゜Д゜)ハァ?

Page 57: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

ちょっと何故ヒットしてるかわかんないですけどwww

Page 58: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

教訓高度に(?)発達した

「だいたいあってる」は「だいたいあってない」

と区別がつかない

Page 59: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

まとめ

Page 60: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

pg_bigmのsimilarityと辞書を使うことで、

完全一致でない検索が出来る。

Page 61: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

なのでpg_bigmにsimilarity機能が正式に取り込まれる

といいなあ・・・

Page 62: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

このゆるい検索の肝はTEXT配列を返却する関数さえあればOK

ということ

Page 63: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

汎用のシソーラス辞書を使うも良し

Web-APIでシソーラスを引っ張ってもOK

(TEXT配列さえ返却すれば)

Page 64: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

最後に

Page 65: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

本当は辞書的なものは使いたくない。

辞書なしでキーワードと文書間でsimilarityを考慮した評価が出来るの

が一番いいのかも。

Page 66: Pgunconf ゆるいテキスト検索ふたたび - n-gram応用編

ご清聴ありがとうございました