Google Colabで日本語の検索とテキスト分析を試す ― nlp4j-local-search 0.5.0
nlp4j-local-search は、Pythonからローカルで全文検索を利用するためのライブラリです。
Elasticsearch、OpenSearch、Solrなどの検索サーバーを別途起動せず、Pythonのコードから検索エンジンを利用できます。
今回は nlp4j-local-search 0.5.0 をGoogle Colabにインストールして、
- 日本語テキストを登録する
- 日本語で全文検索する
-
view()を使って特徴的な単語を調べる
という簡単な例を試してみます。
Google Colabにインストールする
まず、Google Colabで以下を実行します。
!pip install -q nlp4j-local-search==0.5.0
インストールできたら、SearchEngine をインポートします。
from nlp4j_local_search import SearchEngine
これだけで準備完了です。
日本語の検索エンジンを作る
日本語を扱う場合は、
SearchEngine("ja")
と指定します。
簡単な文書を登録して検索してみます。
from nlp4j_local_search import SearchEngine
DOCUMENTS = [
("1", "京都には多くの寺院があります"),
("2", "京都は日本を代表する観光都市です"),
("3", "東京には多くの企業があります"),
("4", "東京は日本の首都です"),
("5", "奈良には古い寺院が多くあります"),
("6", "奈良は歴史的な観光都市です"),
]
with SearchEngine("ja") as engine:
for doc_id, body in DOCUMENTS:
engine.add(doc_id, body)
engine.commit()
results = engine.search("京都", limit=10)
for result in results:
print(result.id, result.body)
このように、通常のPythonコードだけで文書を登録して検索できます。
基本的な流れは、
add()
↓
commit()
↓
search()
です。
検索サーバーを別途起動する必要はありません。
view() でテキストを分析する
nlp4j-local-search 0.5.0 では、検索だけでなく view() を使った簡単なテキスト分析もできます。
たとえば、もう少し文書を増やしてみます。
from nlp4j_local_search import SearchEngine
DOCUMENTS = [
("1", "京都には多くの寺院があります"),
("2", "京都は日本を代表する観光都市です"),
("3", "京都には有名な神社や寺院があります"),
("4", "東京には多くの企業があります"),
("5", "東京は日本の首都です"),
("6", "東京には大きな駅があります"),
("7", "奈良には古い寺院があります"),
("8", "奈良は歴史的な観光都市です"),
("9", "奈良には有名な神社があります"),
("10", "大阪には多くの店があります"),
]
with SearchEngine("ja") as engine:
for doc_id, body in DOCUMENTS:
engine.add(doc_id, body)
engine.commit()
result = engine.view(
query_field="word.noun",
query_value="京都",
field="word.noun",
size=100,
)
print(
"Target documents:",
result.count,
"/",
result.total_count,
)
for bucket in result.buckets:
print(
bucket.key,
bucket.count,
bucket.all_count,
f"{bucket.relative_rate:.4f}",
)
ここでは、
query_field="word.noun"
query_value="京都"
としているので、名詞として「京都」を含む文書を対象にしています。
さらに、
field="word.noun"
として、その文書群に含まれる名詞を集計しています。
relative_rate とは
view() では、それぞれの単語について relative_rate が返されます。
考え方はシンプルです。
relative_rate
=
対象文書における単語の出現率
/
全文書における単語の出現率
たとえば「京都」を含む文書で「寺院」という単語が特に多く現れていれば、「寺院」の relative_rate は高くなります。
逆に、全文書で均等に現れる単語であれば、値は 1.0 に近くなります。
つまり、
relative_rate > 1.0
であれば、その単語は対象となる文書群で相対的によく使われている、と見ることができます。
これは「京都に特徴的な単語は何か?」のような分析に利用できます。
bucket の内容
結果は result.buckets から取得できます。
for bucket in result.buckets:
print(
bucket.key,
bucket.count,
bucket.all_count,
bucket.relative_rate,
)
それぞれ、おおまかには次の意味です。
| プロパティ | 内容 |
|---|---|
key |
単語 |
count |
対象文書内での文書数 |
all_count |
全文書内での文書数 |
relative_rate |
対象文書での相対的な出現率 |
また、
result.count
から対象となった文書数、
result.total_count
から全文書数を取得できます。
検索と分析を同じデータで実行できる
SearchEngine の面白いところは、登録した同じデータに対して、
engine.search(...)
で検索し、
engine.view(...)
で分析できることです。
たとえば、
results = engine.search("京都")
で京都に関する文書を検索し、
result = engine.view(
query_field="word.noun",
query_value="京都",
field="word.noun",
)
で京都に特徴的な名詞を見る、といった使い方ができます。
イメージとしては、
Documents
│
▼
SearchEngine
│
├── search() → 文書を探す
│
└── view() → 文書群を分析する
という形です。
日本語の形態素解析を意識せず使える
今回のコードでは、
engine.add("1", "京都には多くの寺院があります")
と、普通の日本語文字列を渡しているだけです。
その後、
query_field="word.noun"
のように、解析された品詞情報を使うことができます。
そのため、Python側で事前に形態素解析を実行して単語に分割しておく必要はありません。
小規模なテキスト分析であれば、
テキストを登録
↓
日本語解析
↓
インデックス
↓
検索
↓
集計・分析
という処理を一つの SearchEngine から扱えます。
Google Colabとの相性
Google Colabであれば、最初に
!pip install -q nlp4j-local-search==0.5.0
を実行するだけなので、検索エンジンの実験環境をかなり簡単に作れます。
たとえば、
- CSVを読み込む
- JSONLを読み込む
- アンケートの自由記述を分析する
- 製品レビューを検索する
- 問い合わせデータを分析する
- 公開データをテキストマイニングする
といった用途にも応用できます。
まとめ
nlp4j-local-search 0.5.0 を使うと、Google Colab上でも簡単に日本語の検索エンジンを試すことができます。
最小構成なら、
from nlp4j_local_search import SearchEngine
with SearchEngine("ja") as engine:
engine.add("1", "京都には多くの寺院があります")
engine.add("2", "東京には多くの企業があります")
engine.commit()
results = engine.search("京都")
for result in results:
print(result.id, result.body)
だけです。
さらに view() を使えば、
result = engine.view(
query_field="word.noun",
query_value="京都",
field="word.noun",
)
のように、特定の文書群に特徴的な単語も調べることができます。
単に「検索する」だけでなく、
検索した文書を、そのまま分析する
ところまで一つのAPIで扱えるようになってきました。
Google Colabなどを使って、小規模な日本語検索やテキストマイニングを試してみたい場合に便利だと思います。