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 の各メソッドの意味はドキュメントをご参照ください。