概要

LIBLINEARのJava版を使ってみた記事のおまけ。

フレーズ抽出というか、形態素解析結果で次と繋がりやすい言葉を教育して、まとまった単語的なものを取れないか試してみた。

あくまで試してみただけなので、出来栄えは中途半端。ちゃんとやればそこそこの結果は出そうな気はする。

@CretedDate 2012/06/17
@Env LIBLINEAR1.8, kuromoji0.7.7

フロー

Kuromojiを使って形態素解析を行ない、結果に対して手作業で評価を入れる。

行う評価は、それぞれの形態素に対して「次と繋がるなら1、そうでないなら0」を振る、というもの。

行なった評価を使ってtrainをし、モデルを作る。

できたモデルを使っていろんな文書からキーワード抽出をしてみる。

形態素解析

まずはKuromojiで形態素解析を行う。文書はNFKCでUnicode正規化している。

解析結果からは、表層文字列、品詞、未知語判定の3つを取得する。

String line = "文書";
line = Normalizer.normalize(line, Normalizer.Form.NFKC)

Tokenizer tokenizer = Tokenizer.builder().build();
List<Token> tokens = tokenizer.tokenize(line);
for (Token token : tokens) {
  String surface = token.getSurfaceForm();
  String feature = token.getPartOfSpeech();
  boolean unknown = token.isUnknown();
}

結果をタブ区切りでファイルに書き出す。

例えば下記のような文書は

アップル(Apple Inc.)は、アメリカ合衆国カリフォルニア州クパティーノに本社を置く、デジタル家電製品および同製品に関連するソフトウェア製品を研究・設計・製造する多国籍企業である。

下記のような結果として出力する。後で評価する時に楽なように、左に0を置いている。

0	アップル	名詞,一般,*,*	0
0	(	名詞,サ変接続,*,*	1
0	Apple	名詞,固有名詞,組織,*	1
0	 	記号,空白,*,*	1
0	Inc	名詞,固有名詞,組織,*	1
0	.)	名詞,サ変接続,*,*	1
0	は	助詞,係助詞,*,*	0
0	、	記号,読点,*,*	0
0	アメリカ合衆国	名詞,固有名詞,地域,国	0
0	カリフォルニア	名詞,固有名詞,地域,一般	0
0	州	名詞,接尾,地域,*	0
0	クパティーノ	名詞,一般,*,*	1
0	に	助詞,格助詞,一般,*	0
0	本社	名詞,一般,*,*	0
0	を	助詞,格助詞,一般,*	0
0	置く	動詞,自立,*,*	0
0	、	記号,読点,*,*	0
 ・
 ・
 ・

問題点として、2行目の括弧が「名詞,サ変接続」になってしまっている。IPA辞書には半角記号が登録されていないため、未知語推定の結果こうなっている。

名詞,サ変接続の未知語は比較的次と繋がりやすい要素なので、このままだと困る。

今回はKuromojiのユーザ辞書を使ってこの問題を回避する。下記のようなユーザ辞書を作り、Tokenizerのbuild時に指定する。

(,(,(,記号_括弧開
),),),記号_括弧閉
[,[,[,記号_括弧開
],],],記号_括弧閉
{,{,{,記号_括弧開
},},},記号_括弧閉
.,.,.,記号_ピリオド
-,-,-,記号_ハイフン
・,・,・,記号_中黒

ついでなので中黒、ピリオド、ハイフンなど、連結時に大きな意味を持つ文字を別扱いできるよう個別指定している。

結果はこのように変わる。

0	アップル	名詞,一般,*,*	0
0	(	記号_括弧開	0
0	Apple	名詞,固有名詞,組織,*	1
0	 	記号,空白,*,*	1
0	Inc	名詞,固有名詞,組織,*	1
0	.	記号_ピリオド	0
0	)	記号_括弧閉	0

真面目にやる時は辞書本体をカスタマイズしたいところ。

ソースはこんな感じ

手作業で評価する

教師データを作るために、先ほど生成されたデータを手作業でえっさほいさと評価する。

具体的には、下記のように次の形態素と繋がる行は、左端の数値を1に変える。これで次と繋がりやすい条件が教育できる。

0	アップル	名詞,一般,*,*	0
0	(	記号_括弧開	0
1	Apple	名詞,固有名詞,組織,*	1
1	 	記号,空白,*,*	1
1	Inc	名詞,固有名詞,組織,*	1
0	.	記号_ピリオド	0
0	)	記号_括弧閉	0
0	は	助詞,係助詞,*,*	0
0	、	記号,読点,*,*	0
1	アメリカ合衆国	名詞,固有名詞,地域,国	0
1	カリフォルニア	名詞,固有名詞,地域,一般	0
1	州	名詞,接尾,地域,*	0
0	クパティーノ	名詞,一般,*,*	1
0	に	助詞,格助詞,一般,*	0
0	本社	名詞,一般,*,*	0
0	を	助詞,格助詞,一般,*	0
0	置く	動詞,自立,*,*	0
0	、	記号,読点,*,*	0
1	デジタル	名詞,一般,*,*	0
1	家電	名詞,一般,*,*	0
0	製品	名詞,一般,*,*	0
 ・
 ・
 ・

上の例では「Apple Inc.」「デジタル家電製品」などが抽出できることをイメージしている。

こんなデータを10個ほど作ってみた

trainする

作ったデータを使ってLIBLINEARで教育するお時間です。

今回は後方5つと前方1つの形態素を見て、それとの繋がりやすさから結果を推定してみる。後方5つも必要かと言われると不明。何ごともノリとタイミング。

以下のような値を利用して教育を施す。

1, 文字列の長さ
2, 未知語(0/1)
3, 文字列内のその他文字率
4, 文字列内の記号率
5, 文字列内の数字率
6, 文字列内のアルファベット率
7, 文字列内の平仮名率
8, 文字列内のカタカナ率
9, 文字列内の漢字率
10〜99, 品詞(名詞,一般なら49に1を立てる、動詞,自立なら42に1を立てる、みたいな) 

品詞は下記のようなファイルを用意して数値に変換して、11〜99の間に当てはめている。

1	その他,間投,*,*
2	フィラー,*,*,*
3	感動詞,*,*,*
4	記号,アルファベット,*,*
5	記号,一般,*,*
5	記号_一般

1〜100に自身、101〜200に次の形態素、201〜300にその次の形態素の情報と入れていき、501〜600までに1つ手前の形態素の情報を入れる。

最後(601番)に、1つ前のデータの評価が1かどうかを付け加えておく。

これらのデータを自身、次の形態素、その次の形態素と5つ並べていき、こんなデータにする。

1 1:1 2:0 9:1.0 49:1 101:1 102:0 109:1.0 162:1 201:1 202:0 204:1.0 216:1 301:2 302:0 307:1.0 342:1 401:2 402:0 407:1.0 444:1
0 1:1 2:0 9:1.0 62:1 101:1 102:0 104:1.0 116:1 201:2 202:0 207:1.0 242:1 301:2 302:0 307:1.0 344:1 401:1 402:0 404:1.0 420:1
0 1:1 2:0 4:1.0 16:1 101:2 102:0 107:1.0 142:1 201:2 202:0 207:1.0 244:1 301:1 302:0 304:1.0 320:1 401:4 402:0 407:1.0 442:1 501:1 502:0 509:1.0 562:1 601:0
1 1:2 2:0 7:1.0 42:1 101:2 102:0 107:1.0 144:1 201:1 202:0 204:1.0 220:1 301:4 302:0 307:1.0 342:1 401:1 402:0 404:1.0 417:1 501:1 502:0 504:1.0 516:1 601:0
0 1:2 2:0 7:1.0 44:1 101:1 102:0 104:1.0 120:1 201:4 202:0 207:1.0 242:1 301:1 302:0 304:1.0 317:1 401:1 402:0 407:1.0 425:1 501:2 502:0 507:1.0 542:1 601:1
0 1:1 2:0 4:1.0 20:1 101:4 102:0 107:1.0 142:1 201:1 202:0 204:1.0 217:1 301:1 302:0 307:1.0 325:1 401:1 402:0 407:1.0 427:1 501:2 502:0 507:1.0 544:1 601:0
0 1:4 2:0 7:1.0 42:1 101:1 102:0 104:1.0 117:1 201:1 202:0 207:1.0 225:1 301:1 302:0 307:1.0 327:1 401:1 402:0 404:1.0 420:1 501:1 502:0 504:1.0 520:1 601:0
0 1:1 2:0 4:1.0 17:1 101:1 102:0 107:1.0 125:1 201:1 202:0 207:1.0 227:1 301:1 302:0 304:1.0 320:1 401:1 402:0 409:1.0 449:1 501:4 502:0 507:1.0 542:1 601:0

このファイルをtrainにかけて、modelを生成する。

ソースはこんな感じ

predictする

出来上がったmodelを使って、WikipediaのBSDライセンスのページに対してpredictしてみたところ、下記のようなフレーズが取れた。

無 保証
著作 権
ライセンス 条文 自身
再 頒布
ライセンス 規定
BSD ライセンス
ソース コード
オブジェクト コード
ソース コード
著作 権
ライセンス 条文
無 保証
の 三 点
ドキュメント 等
BSD ライセンス
ソース コード
組み込み 後
ソース コード
再 配布 時
ライセンス 条件
商用 化
標準 規格
BSD ライセンス
派生 物
初期 開発 者
宣伝 条項
BSD ライセンス
修正 BSD ライセンス
New   BSD   License
三 条項 BSD ライセンス
3 - clause
  license
NetBSD オペレーティングシステム
た 二 条項 BSD ライセンス
2 - clause
  license
宣伝 条項
BSD ライセンス
旧 BSD ライセンス
四 条項 BSD ライセンス
4 - clause
  license
旧 BSD ライセンス
2 次 的
著作 物
制限 」" further
  restrictions
BSD スタイル
ソース コード
コピー レフト

ところどころ変なのもいるけど、そこそこ取れてる。スペースの扱いがなんか良くないっぽい。

ピカチュウのページに対してかけたら、体重(6.0kg)がうまく取れず0kgで抽出してしまっていた。ピリオドが数字の間に入るパターンを学習させた覚えがないので、単純な教育不足だと思われる。

数字の途中にカンマが付く(1,000円みたいな)パターンも出くわしてないので、おそらくうまくいかないと予想される。

ソースはこんな感じ

反省点

キーフレーズ抽出ってことなら、頻出度も用いてスコア付けとかしないといけない。

学習元がWikipediaの記事10個くらいだと若干ガッツが足りない。最初5個でやって10個にしたらだいぶ改善したので、20個にしたらもう少しまともになるかも。

真面目にやるならパラメータいろいろいじって結果を検証するフェーズを設けないといけない。それはとても面倒。

そもそも何をもってフレーズとするかみたいなことを厳密に決めてなかった。

記号はそれぞれ意味合いが変わるので、もう少しちゃんと指定しておきたい。ユーザ辞書に指定しても形態素解析の都合で名詞などと判定されることがあって、妙な連結をしてしまうことがある。

IPA辞書だと新聞向けなので一般的なウェブサイトで利用すると精度が少し落ちる。解析精度が落ちると、当然抽出結果にも影響する。

思わず自前でWikipedia用の辞書を作り始めてみたけど、途中でゲシュタルト崩壊が起きて何が何やら分からなくなった。

ソース

ソースはこちらに置いておいた。

一応、mavenで動く。

// コンパイル
$ mvn compile

// フレーズ抽出
$ mvn exec:java -Dexec.args="extract test-data/bsd_license"

// 形態素解析(結果は同一フォルダに末尾に.out付きで出力される)
$ mvn exec:java -Dexec.args="parse test-data/bsd_license"

// train(trainディレクトリにいるファイルを使って教育する。結果はmodelディレクトリに出力)
$ mvn exec:java -Dexec.args="train"