MeCab で N-Best 解の累積コストを出力する
能動学習の考え方を適用すると、MeCab の学習データを作成する際には第 1 候補と第 2 候補の累積コストの差が小さいものを優先的にアノテーションすれば良さそうな気がします。
ところが、MeCab では普通に N-Best の累積コストを出力しようとしても出力できません。
cf. MeCabでN-Best解を出力した時のコストの表示がおかしい? - 唯物是真 @Scaled_Wurm
何とかならないものかと考えてみたんですが、制約付き解析と組み合わせることで一応出力できるみたいです。
まず、普通に N-Best 解を出力します。
% echo すもももももももものうち | mecab -N 2
すもも 名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もも 名詞,一般,*,*,*,*,もも,モモ,モモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もも 名詞,一般,*,*,*,*,もも,モモ,モモ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
うち 名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
EOS
すもも 名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もも 名詞,一般,*,*,*,*,もも,モモ,モモ
もも 名詞,一般,*,*,*,*,もも,モモ,モモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
うち 名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
EOS
次に、制約付き解析でそれぞれの結果に対して累積コストを出力します。
% mecab -p -F '%m\t%H\t%pw,%pC,%pc\n' -E 'EOS\t%pw,%pC,%pc\n'
すもも 名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もも 名詞,一般,*,*,*,*,もも,モモ,モモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もも 名詞,一般,*,*,*,*,もも,モモ,モモ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
うち 名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
EOS
すもも 名詞,一般,*,*,*,*,すもも,スモモ,スモモ 7546,-283,7263
も 助詞,係助詞,*,*,*,*,も,モ,モ 4669,-4158,7774
もも 名詞,一般,*,*,*,*,もも,モモ,モモ 7219,17,15010
も 助詞,係助詞,*,*,*,*,も,モ,モ 4669,-4158,15521
もも 名詞,一般,*,*,*,*,もも,モモ,モモ 7219,17,22757
の 助詞,連体化,*,*,*,*,の,ノ,ノ 4816,-4442,23131
うち 名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ 5796,-5198,23729
EOS 0,-2484,21245
すもも 名詞,一般,*,*,*,*,すもも,スモモ,スモモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
もも 名詞,一般,*,*,*,*,もも,モモ,モモ
もも 名詞,一般,*,*,*,*,もも,モモ,モモ
も 助詞,係助詞,*,*,*,*,も,モ,モ
の 助詞,連体化,*,*,*,*,の,ノ,ノ
うち 名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
EOS
すもも 名詞,一般,*,*,*,*,すもも,スモモ,スモモ 7546,-283,7263
も 助詞,係助詞,*,*,*,*,も,モ,モ 4669,-4158,7774
もも 名詞,一般,*,*,*,*,もも,モモ,モモ 7219,17,15010
もも 名詞,一般,*,*,*,*,もも,モモ,モモ 7219,62,22291
も 助詞,係助詞,*,*,*,*,も,モ,モ 4669,-4158,22802
の 助詞,連体化,*,*,*,*,の,ノ,ノ 4816,-1191,26427
うち 名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ 5796,-5198,27025
EOS 0,-2484,24541
上手くいってそうですね!(コードを追ったわけではないので正しい保証はありません)
Ruby でやってみる
上記の手順だと面倒くさいので Ruby でやってみましょう。
#!/usr/bin/env ruby
require 'mecab'
tagger = MeCab::Tagger.new('-N 2')
tagger.parseNBestInit('すもももももももものうち')
partial_tagger = MeCab::Tagger.new('-p')
costs = []
2.times do
node = partial_tagger.parseToNode(tagger.next)
node = node.next until node.stat == MeCab::MECAB_EOS_NODE
costs << node.cost
end
puts "margin: #{costs[1] - costs[0]}"
% ruby /path/to/script
margin: 3296
いい感じですねっ!!
tagger の各メソッドの意味はドキュメントをご参照ください。