一般にはタグ付けは手動で行うわけですが、自然言語テキストへのタグ付け(キーワード抽出)を自動で行うことができれば、あれこれと可能性が広がって楽しそう……しかし、それは実現が難しかったり高コストだったりして、簡単に手を出せる解はあまりありません。
ラボの奥さんの作成したキーワード抽出モジュール Lingua::JA::Summarize は次の特徴を持っています。
- 動作要件の敷居が低い
- 辞書のメンテナンスをしなくても、未知語や熟語もある程度抽出してくれる
- 希望の結果に近づけるためのチューニングが可能
そういったコストパフォーマンスのいいキーワード抽出モジュールなんですが、あまり使われている様子がなくもったいない。 そこでQ&A式で使い方やコツをご紹介します。
- Q1: Lingua::JA::Summarize を使うには何が必要?
- Q2:実際にキーワード抽出を行うサンプルコードある?
- Q3:「こと」とか「とき」といった不要な単語がキーワードとして抽出されてしまうんだけど……
- Q4:文字コードに UTF-8, Shift_JIS, ... を使いたい
- Q5:出てきて欲しいキーワードがどうしても出てこないんだけど……
A1:
Perl と形態素解析エンジンの MeCab をあらかじめインストールしてください。
Lingua::JA::Summarize 本体は CPAN で配布されているので、通常の方法でインストールしてください。
$ perl -MCPAN -e shell > install Lingua::JA::Summarize現在(2007/03/16)配布されている Lingua::JA::Summarize のバージョンは 0.06 ですが、近々 0.07 が出るはず。(※ 3/19 追記:0.07 出ました)
これは以下の2点を対応したものになっています。
- MeCab 0.94 対応
- 英単語の生成コストを指定できるようになった
MeCab 0.93 では、まれにテキストによっては文字化けが発生することがあるのを確認しています。抽出されるキーワードも 0.94 の方が良い感じがする(注:主観)ので、できるだけ 0.94 以降を使用されることをおすすめします。
また、MeCab の辞書としては JUMAN と IPADIC が配布されていますが、どちらの辞書を使ってもかまいません。解析結果を比べてみた範囲では IPADIC の方が若干好ましい結果が出ているように感じました(注:主観)。
Q2:実際にキーワード抽出を行うサンプルコードある?
A2:
標準入力に与えられたテキスト(EUC-JP)からキーワードを抽出して出力するサンプルです。
use strict; use warnings; use Lingua::JA::Summarize; my $text = join('',<>); # 解析対象のテキスト my $s = Lingua::JA::Summarize->new({ default_cost => 1.8, singlechar_factor => 0.2, }); $s->analyze($text); my @keywords = $s->keywords({threshold=>4, maxwords=>10, minwords=>3}); print join(' ', @keywords), "\n";最低限のチューニング用パラメータが入っていますので、個々に説明。
- default_cost
- 未知語の重み付けを変更します。0 以上の値を指定でき、デフォルト値は 1 です。出力を見ながら 1~2 あたりで調整してみましょう
- singlechar_factor
- 1文字の単語の重み付けを変更します。デフォルト値は 0.5 です。default_cost を増やすと1文字の単語が拾われやすくなってしまうため、少し小さめの値を指定しておきます
- threshold
- キーワード抽出の閾値。大きいほどキーワードを厳選(抽出されにくい)します。デフォルト値は 5。思うように抽出されない場合は threshold を下げてみましょう
- maxwords, minwords
- 抽出するキーワード数の上限&下限です。デフォルト値は 5 と 0。
Q3:「こと」とか「とき」といった不要な単語がキーワードとして抽出されてしまうんだけど……
A3:
不要なキーワードは NG ワードに指定することではじくことができます。
NG ワードの指定は下記のように行ってください。
my %ng = map { $_ => 1 } ( '(', ')', '#', ',', '"', "'", '`', qw(! $ % & * + - . / : ; < = > ? @ [ \ ] ^ _ { | } ~ 人 秒 分 時 日 月 年 円 ドル 一 二 三 四 五 六 七 八 九 十 百 千 万 億 兆 ↑ ↓ ← → ⇒ ⇔ もの こと とき ため ほう そこ ここ とか ところ こちら こんな 私 僕 俺 自分 彼 彼ら 誰 何), # NGワードを空白区切りで追加 ); my $s = Lingua::JA::Summarize->new({ default_cost => 1.8, singlechar_factor => 0.2, ng => \%ng, # 追加 });なお新しく NG ワードを設定する場合には上書きになってしまうため、本サンプルにはデフォルトの NG ワードも含んでいます。
Q4:文字コードに UTF-8, Shift_JIS, ... を使いたい
A4:
Lingua::JA::Summarize のデフォルト文字コードは EUC-JP なので、それ以外の文字コードを使う場合には Lingua::JA::Summarize->new に明示的に文字コードを指定する必要があります。
以下は UTF-8 を使う場合のサンプルです。NG ワード定義の記述も若干変わっています。
use Jcode; my %ng = map { Jcode::convert($_, 'euc', 'utf-8') => 1 } ( # 文字コード '(', ')', '#', ',', '"', "'", '`', qw(! $ % & * + - . / : ; < = > ? @ [ \ ] ^ _ { | } ~ 人 秒 分 時 日 月 年 円 ドル 一 二 三 四 五 六 七 八 九 十 百 千 万 億 兆 ↑ ↓ ← → ⇒ ⇔ もの こと とき ため ほう そこ ここ とか ところ こちら こんな 私 僕 俺 自分 彼 彼ら 誰 何), ); my $s = Lingua::JA::Summarize->new({ charset => 'utf8', # 追加 mecab_charset => 'euc', # 追加(MeCabの文字コード) default_cost => 1.8, singlechar_factor => 0.2, ng => \%ng, # 追加 });
Q5:出てきて欲しいキーワードがどうしても出てこないんだけど……
A5:
Lingua::JA::Summarize は未知語を(おおむね期待以上に)拾ってくれますが、さすがに完璧ではありません。
多いパターンとしては、拾って欲しいキーワードが抽出されなかったり、単語の切れ目が期待通りではない場合などでしょうか。
そういった問題には、MeCab の辞書に単語を追加することで対応できます。
辞書のビルド手順そのものは 「MeCab: 単語の追加方法」 を参照していただくとして、ここでは Lingua::JA::Summarize を使うためにどういった形で MeCab の単語ファイル(user.csv)を作ればよいかを説明します。
未知語,1647,1647,5000,名詞,普通名詞,*,*,未知語,みちご,* Japanize,1641,1641,8000,名詞,固有名詞,*,*,Japanize,japanize,*追加したい単語1つに対して、上記のような1行を記述します。
2番目&3番目のパラメータは単語の品詞情報に対応する番号です。登録したい単語に応じて次の番号の中から選んでつけてやってください。
1641 名詞,固有名詞 1643 名詞,人名 1645 名詞,組織名 1646 名詞,地名 1647 名詞,普通名詞Lingua::JA::Summarize のために追加するような単語はだいたいこの中に含まれてくると思いますが、なければ辞書パッケージの中にある right-id.def などを参照してください。
4番目のパラメータは「生起コスト」です。Lingua::JA::Summarize では、この値が大きいほど「珍しい単語」=「キーワードとして抽出されやすい」という扱いをします。だいたい 5000~8000 の間で、解析結果を見ながら調整しましょう。
また英単語を追加する場合、解析結果ではすべて小文字に変換されて出力されますが、単語の定義時にはテキスト内に記述されている表記で登録する必要があります。