ELIZA NG(3)Rakuten MAでmecab-ipadic-neologdを学習する

How to use Rakuten MA, which may be a little difficult for monkeys


2019/12/26
藤田昭人


クリスマスに間に合わなかった…

前回 は Rakuten MA の基本的な使い方について紹介しました。 非常にコンパクトな形態素解析器として Rakuten MA は便利なライブラリです。 が、実際に形態素解析を行うと、その結果に「あれ?」と思う事が稀にあります。 「おそらく付属の日本語解析モデルに問題があるのだろう…」 と想像した僕は 「定評のある mecab-ipadic-neologd を使えないものだろうか?」 と考えました。

本稿ではその方法を紹介します。


mecabmecab-ipadic-neologd について

ということで、まずは mecabmecab-ipadic-neologd を動かさないといけないのですが…

定番の形態素解析システムである mecabmecab-ipadic-neologd について、 ここで改めて紹介することはないでしょう。各々のホームページ(?)は以下のようです。

taku910.github.io

github.com

そのインストール方法もはてなブログや Qiita に多数記事があるので、 ご自分の環境に合わせて適当な記事を参考にしてください。 ちなみに Mac Book Pro を使っている僕は次の記事の手順にしたがってインストールしました。

qiita.com

今のところ特に問題は出ていません。

必要なセットアップのあと、次のとおり mecab を動かしてみました。 例文は前回紹介した「やさしい日本語」コーパスの冒頭の3文を使いましたが、 NEologd が動いていることを確かめたくて一部を「渡辺謙」に差し替えました。

$ cat test.txt
誰が一番に着くか私には分かりません。
私は渡辺謙です。
エミは幸せそうに見えます。
$ mecab -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd test.txt
誰 名詞,代名詞,一般,*,*,*,誰,ダレ,ダレ
が 助詞,格助詞,一般,*,*,*,が,ガ,ガ
一番  名詞,副詞可能,*,*,*,*,一番,イチバン,イチバン
に 助詞,格助詞,一般,*,*,*,に,ニ,ニ
着く  動詞,自立,*,*,五段・カ行イ音便,基本形,着く,ツク,ツク
か 助詞,副助詞/並立助詞/終助詞,*,*,*,*,か,カ,カ
私 名詞,代名詞,一般,*,*,*,私,ワタシ,ワタシ
に 助詞,格助詞,一般,*,*,*,に,ニ,ニ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
分かり   動詞,自立,*,*,五段・ラ行,連用形,分かる,ワカリ,ワカリ
ませ  助動詞,*,*,*,特殊・マス,未然形,ます,マセ,マセ
ん 助動詞,*,*,*,不変化型,基本形,ん,ン,ン
。 記号,句点,*,*,*,*,。,。,。
EOS
私 名詞,代名詞,一般,*,*,*,私,ワタシ,ワタシ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
渡辺謙   名詞,固有名詞,人名,一般,*,*,渡辺謙,ワタナベケン,ワタナベケン
です  助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
。 記号,句点,*,*,*,*,。,。,。
EOS
エミ  名詞,固有名詞,人名,名,*,*,エミ,エミ,エミ
は 助詞,係助詞,*,*,*,*,は,ハ,ワ
幸せ  名詞,形容動詞語幹,*,*,*,*,幸せ,シアワセ,シアワセ
そう  名詞,接尾,助動詞語幹,*,*,*,そう,ソウ,ソー
に 助詞,副詞化,*,*,*,*,に,ニ,ニ
見え  動詞,自立,*,*,一段,連用形,見える,ミエ,ミエ
ます  助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス
。 記号,句点,*,*,*,*,。,。,。
EOS
$

ちなみに -d オプションで指定する NEologd のインストール位置はインストール時に任意に選べますので、 実際にどこへインストールされているかは mecab-config --dicdir コマンドで確かめてください。


ちょっと試してみた

実は当初は mecab が出力する品詞情報を Rakuten MA が使用する日本語品詞タグと対応づけることを考えたのですが、 Rakuten MA の日本語品詞タグの定義を解説するドキュメントがなかなか見つけられず*1… そのうち面倒臭くなってきて「この際、まんまブッ込んでみたら案外上手くいくかも…」と思い立ち、 次のようなコードを書いたのでした。

var fs = require('fs');
var RakutenMA = require('rakutenma');
var rma = new RakutenMA();
rma.featset = RakutenMA.default_featset_ja;

var mecab = [
  [
    [ "誰", "名詞,代名詞,一般,*,*,*,誰,ダレ,ダレ" ],
    [ "が", "助詞,格助詞,一般,*,*,*,が,ガ,ガ" ],
    [ "一番", "名詞,副詞可能,*,*,*,*,一番,イチバン,イチバン" ],
    [ "に", "助詞,格助詞,一般,*,*,*,に,ニ,ニ" ],
    [ "着く", "動詞,自立,*,*,五段・カ行イ音便,基本形,着く,ツク,ツク" ],
    [ "か", "助詞,副助詞/並立助詞/終助詞,*,*,*,*,か,カ,カ" ],
    [ "私", "名詞,代名詞,一般,*,*,*,私,ワタシ,ワタシ" ],
    [ "に", "助詞,格助詞,一般,*,*,*,に,ニ,ニ" ],
    [ "は", "助詞,係助詞,*,*,*,*,は,ハ,ワ" ],
    [ "分かり", "動詞,自立,*,*,五段・ラ行,連用形,分かる,ワカリ,ワカリ" ],
    [ "ませ", "助動詞,*,*,*,特殊・マス,未然形,ます,マセ,マセ" ],
    [ "ん", "助動詞,*,*,*,不変化型,基本形,ん,ン,ン" ],
    [ "。", "記号,句点,*,*,*,*,。,。,。" ]
  ],
  [
    [ "私", "名詞,代名詞,一般,*,*,*,私,ワタシ,ワタシ" ],
    [ "は", "助詞,係助詞,*,*,*,*,は,ハ,ワ" ],
    [ "渡辺謙", "名詞,固有名詞,人名,一般", "*,*,渡辺謙,ワタナベケン,ワタナベケン" ],
    [ "です", "助動詞,*,*,*,特殊・デス,基本形,です,デス,デス" ],
    [ "。", "記号,句点,*,*,*,*,。,。,。" ]
  ],
  [
    [ "エミ", "名詞,固有名詞,人名,名,*,*,エミ,エミ,エミ" ],
    [ "は", "助詞,係助詞,*,*,*,*,は,ハ,ワ" ],
    [ "幸せ", "名詞,形容動詞語幹,*,*,*,*,幸せ,シアワセ,シアワセ" ],
    [ "そう", "名詞,接尾,助動詞語幹,*,*,*,そう,ソウ,ソー" ],
    [ "に", "助詞,副詞化,*,*,*,*,に,ニ,ニ" ],
    [ "見え", "動詞,自立,*,*,一段,連用形,見える,ミエ,ミエ" ],
    [ "ます", "助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス" ],
    [ "。", "記号,句点,*,*,*,*,。,。,。" ]
  ]
];

for (var j = 0; j < 2; j++) {
  for (var i = 0; i < mecab.length; i ++) {
    rma.train_one(mecab[i]);
  }
}

console.log(rma.tokenize("誰が一番に着くか私には分かりません。"));
console.log(rma.tokenize("私は渡辺謙です。"));
console.log(rma.tokenize("エミは幸せそうに見えます。"));

で、これを動かしてみたら…

$ node TinyTest.js
[ [ '誰', '名詞,代名詞,一般,*,*,*,誰,ダレ,ダレ' ],
  [ 'が', '助詞,格助詞,一般,*,*,*,が,ガ,ガ' ],
  [ '一番', '名詞,副詞可能,*,*,*,*,一番,イチバン,イチバン' ],
  [ 'に', '助詞,格助詞,一般,*,*,*,に,ニ,ニ' ],
  [ '着く', '動詞,自立,*,*,五段・カ行イ音便,基本形,着く,ツク,ツク' ],
  [ 'か', '助詞,副助詞/並立助詞/終助詞,*,*,*,*,か,カ,カ' ],
  [ '私', '名詞,代名詞,一般,*,*,*,私,ワタシ,ワタシ' ],
  [ 'に', '助詞,格助詞,一般,*,*,*,に,ニ,ニ' ],
  [ 'は', '助詞,係助詞,*,*,*,*,は,ハ,ワ' ],
  [ '分かり', '動詞,自立,*,*,五段・ラ行,連用形,分かる,ワカリ,ワカリ' ],
  [ 'ませ', '助動詞,*,*,*,特殊・マス,未然形,ます,マセ,マセ' ],
  [ 'ん', '助動詞,*,*,*,不変化型,基本形,ん,ン,ン' ],
  [ '。', '記号,句点,*,*,*,*,。,。,。' ] ]
[ [ '私', '名詞,代名詞,一般,*,*,*,私,ワタシ,ワタシ' ],
  [ 'は', '助詞,係助詞,*,*,*,*,は,ハ,ワ' ],
  [ '渡辺謙', '名詞,固有名詞,人名,一般' ],
  [ 'です', '助動詞,*,*,*,特殊・デス,基本形,です,デス,デス' ],
  [ '。', '記号,句点,*,*,*,*,。,。,。' ] ]
[ [ 'エミ', '名詞,固有名詞,人名,名,*,*,エミ,エミ,エミ' ],
  [ 'は', '助詞,係助詞,*,*,*,*,は,ハ,ワ' ],
  [ '幸せ', '名詞,形容動詞語幹,*,*,*,*,幸せ,シアワセ,シアワセ' ],
  [ 'そう', '名詞,接尾,助動詞語幹,*,*,*,そう,ソウ,ソー' ],
  [ 'に', '助詞,副詞化,*,*,*,*,に,ニ,ニ' ],
  [ '見え', '動詞,自立,*,*,一段,連用形,見える,ミエ,ミエ' ],
  [ 'ます', '助動詞,*,*,*,特殊・マス,基本形,ます,マス,マス' ],
  [ '。', '記号,句点,*,*,*,*,。,。,。' ] ]
$ 

この出力を見て「あ、なんか上手くいってるのかも?」などと思ったのですが…
世の中、そんなには甘くない。


IPADICの品詞IDの話

そこで「やさしい日本語」コーパスの5万文について mecab-ipadic-neologd の出力を品詞情報とした学習データを作成し、 前回紹介した「オリジナルの解析モデルの学習」のコードに突っ込んでみたのですが…学習にムッチャ時間がかかるので断念。 文例が3つ、4つでは問題にはならない訳ですが、さすがに5万文もあると無理なようです。

そこで更に調べてみたところ…

文字どおり mecab-ipadic-neologd は IPADIC の IPA品詞体系に依拠しているそうで、 出力の最初の4項目は「品詞、品詞再分類1、品詞再分類2、品詞再分類3」を示すそうです。 詳細は次のページをご覧ください。

hayashibe.jp

mecab-ipadic-neologd の品詞情報は "/usr/local/lib/mecab/dic/mecab-ipadic-neologd/pos-id.def" で定義され、全部で69種類しかないそうです。 ちなみに定義ファイル pos-id.def の中身は次のとおり。

その他,間投,*,* 0
フィラー,*,*,* 1
感動詞,*,*,* 2
記号,アルファベット,*,* 3
記号,一般,*,* 4
記号,括弧開,*,* 5
記号,括弧閉,*,* 6
記号,句点,*,* 7
記号,空白,*,* 8
記号,読点,*,* 9
形容詞,自立,*,* 10
形容詞,接尾,*,* 11
形容詞,非自立,*,* 12
助詞,格助詞,一般,* 13
助詞,格助詞,引用,* 14
助詞,格助詞,連語,* 15
助詞,係助詞,*,* 16
助詞,終助詞,*,* 17
助詞,接続助詞,*,* 18
助詞,特殊,*,* 19
助詞,副詞化,*,* 20
助詞,副助詞,*,* 21
助詞,副助詞/並立助詞/終助詞,*,* 22
助詞,並立助詞,*,* 23
助詞,連体化,*,* 24
助動詞,*,*,* 25
接続詞,*,*,* 26
接頭詞,形容詞接続,*,* 27
接頭詞,数接続,*,* 28
接頭詞,動詞接続,*,* 29
接頭詞,名詞接続,*,* 30
動詞,自立,*,* 31
動詞,接尾,*,* 32
動詞,非自立,*,* 33
副詞,一般,*,* 34
副詞,助詞類接続,*,* 35
名詞,サ変接続,*,* 36
名詞,ナイ形容詞語幹,*,* 37
名詞,一般,*,* 38
名詞,引用文字列,*,* 39
名詞,形容動詞語幹,*,* 40
名詞,固有名詞,一般,* 41
名詞,固有名詞,人名,一般 42
名詞,固有名詞,人名,姓 43
名詞,固有名詞,人名,名 44
名詞,固有名詞,組織,* 45
名詞,固有名詞,地域,一般 46
名詞,固有名詞,地域,国 47
名詞,数,*,* 48
名詞,接続詞的,*,* 49
名詞,接尾,サ変接続,* 50
名詞,接尾,一般,* 51
名詞,接尾,形容動詞語幹,* 52
名詞,接尾,助数詞,* 53
名詞,接尾,助動詞語幹,* 54
名詞,接尾,人名,* 55
名詞,接尾,地域,* 56
名詞,接尾,特殊,* 57
名詞,接尾,副詞可能,* 58
名詞,代名詞,一般,* 59
名詞,代名詞,縮約,* 60
名詞,動詞非自立的,*,* 61
名詞,特殊,助動詞語幹,* 62
名詞,非自立,一般,* 63
名詞,非自立,形容動詞語幹,* 64
名詞,非自立,助動詞語幹,* 65
名詞,非自立,副詞可能,* 66
名詞,副詞可能,*,* 67
連体詞,*,*,* 68

各行の右端にあるのが品詞ID。 詳細は mecab のマニュアルの次のページに書いてありました。

taku910.github.io

つまり、次のようなコマンドオプションを指定すると mecab は品詞IDを出力してくれるそうです。

$ mecab -F"%m\t%h\n" -E"EOS\n" 
今日もしないとね。
今日    67
も      16
し      31
ない    25
と      18
ね      17
。
EOS
$ 

…ってことは、Rakuten MAの日本語品詞タグの代わりにmecabの品詞IDを格納すれば良いってことになりますね。


mecab-ipadic-neologd を使ってRakuten MAの辞書を作る

と言うことで…

前回紹介したRakuten MAのモデル生成手順を一部修正して、 mecab-ipadic-neologd による解析モデルを生成してみます。

学習データの生成: mecab+mecab-ipadic-neologd で形態素解析

まず、前回紹介した「やさしい日本語」コーパスから mecab 用の入力ファイル "T15-2018.2.28-easy.txt" を作成して、次のコマンドを実行し、 "T15-2018.2.28-mecab-posid.txt" ました。

$ mecab -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd -F"%m\t%h\n" -E"EOS\n"  T15-2018.2.28-easy.txt > T15-2018.2.28-mecab-posid.txt
$ head -20 T15-2018.2.28-mecab-posid.txt
誰 59
が 13
一番  67
に 13
着く  31
か 22
私 59
に 13
は 16
分かり   31
ませ  25
ん 25
。 7
EOS
多く  67
の 24
動物  38
が 13
人間  38
によって    15
$ 

次に、この出力ファイルを取り込んで JSON フォーマットの学習データを生成する "mecabid2json.js" を作成しました。コードは次のとおりです。

var fs = require('fs');
const name = "T15-2018.2.28-mecab-posid";
var whole = fs.readFileSync(name+".txt");
var lines = whole.toString().split('\n');

var out = [];
var sentence = [];
for (var i = 0; i < lines.length; i++) {
    var line = lines[i];
    //console.log(("     "+i).slice(-5)+": "+line);
    if (line == "EOS") {
        out.push(sentence);
        sentence = [];
    } else {
        var tokens = line.split('\t');
        var elm = [];
        elm[0] = tokens[0];
        elm[1] = tokens[1];
        sentence.push(elm);
    }
}

fs.writeFile(name+".json", JSON.stringify(out, undefined, 2), 
function(err, result) {
    if (err) console.log('error', err);
});

ここまでくれば、あとは前回紹介した手順で解析モデルを生成できますね。 繰り返しになりますが、コードを紹介しておくと…


オリジナルの解析モデルの学習

前項で生成した "T15-2018.2.28-mecab-posid.json" を使って、オリジナルの解析モデルを生成します。 次のオンライン機械学習をするコードを書きました。

var fs = require('fs');
var RakutenMA = require('rakutenma');
rma = new RakutenMA();
rma.featset = RakutenMA.default_featset_ja;
rma.hash_func = RakutenMA.create_hash_func(15);

var rcorpus = JSON.parse(fs.readFileSync("T15-2018.2.28-mecab-posid.json"));

console.error( "total sents = " + rcorpus.length );

const iter = 1;

for (var k = 0; k < iter; k ++) {
    console.error("iter = " + k);
    for (var i = 0; i < rcorpus.length; i++) {
        var res = rma.train_one(rcorpus[i]);
        if (i % 100 == 0)
            console.error("  train i = " + i);
    }
}

fs.writeFile("learn-neologd.json", JSON.stringify(rma.model), 
function(err, result) {
    if (err) console.log('error', err);
});

生成される解析モデル "learn-neologd.json" はRakuten MAに添付される "model_ja.json" と同様に使えますし、 前回紹介したモデル圧縮の "minify.js" も使えますが、 語句と共に出力されるのは mecab の品詞ID であることに注意してください。

var whole = [
    "誰が一番に着くか私には分かりません。",
    "私は渡辺謙です。",
    "エミは幸せそうに見えます。"
];

var fs = require('fs');
var RakutenMA = require('rakutenma');
var model = JSON.parse(fs.readFileSync("learn-neologd.json"));
var rma = new RakutenMA(model, 1024, 0.007812);
rma.featset = RakutenMA.default_featset_ja;
rma.hash_func = RakutenMA.create_hash_func(15);

for (var i = 0; i < whole.length; i++) {
    console.log(rma.tokenize(whole[i]));
}

例えば、上記のコードを実行すると、次のようになります。

$ node print-neologd.js
[ [ '誰', '59' ],
  [ 'が', '13' ],
  [ '一番', '67' ],
  [ 'に', '13' ],
  [ '着く', '31' ],
  [ 'か', '22' ],
  [ '私', '59' ],
  [ 'に', '13' ],
  [ 'は', '16' ],
  [ '分かり', '31' ],
  [ 'ませ', '25' ],
  [ 'ん', '25' ],
  [ '。', '7' ] ]
[ [ '私', '59' ],
  [ 'は', '16' ],
  [ '渡辺謙', '41' ],
  [ 'です', '25' ],
  [ '。', '7' ] ]
[ [ 'エミ', '44' ],
  [ 'は', '16' ],
  [ '幸せ', '40' ],
  [ 'そう', '54' ],
  [ 'に', '20' ],
  [ '見え', '31' ],
  [ 'ます', '25' ],
  [ '。', '7' ] ]
$ 

ちなみに同じデータをmecabで品詞IDを表示させると…

$ cat test.txt
誰が一番に着くか私には分かりません。
私は渡辺謙です。
エミは幸せそうに見えます。
$ mecab -d /usr/local/lib/mecab/dic/mecab-ipadic-neologd -F"%m\t%h\n" -E"EOS\n" test.txt
誰 59
が 13
一番  67
に 13
着く  31
か 22
私 59
に 13
は 16
分かり   31
ませ  25
ん 25
。 7
EOS
私 59
は 16
渡辺謙   42
です  25
。 7
EOS
エミ  44
は 16
幸せ  40
そう  54
に 20
見え  31
ます  25
。 7
EOS
$ 

どうやら上手くいったようです。


おわりに

本稿では Rakuten MA で mecab-ipadic-neologd を使う方法を紹介しました。

ちなみに全部で5万文の「やさしい日本語」コーパスについて Rakuten MA のオリジナルの解析モデルと mecab-ipadic-neologd による解析モデルの各々を使って 分かち書きをしてみたところ 17809 文(約36%)では相違点が見つかりました。 その例を少し示すと…

1: 多くの動物が人間によって殺された。
[ [ '多く', 'N-nc' ],
  [ 'の', 'P-k' ],
  [ '動物', 'N-nc' ],
  [ 'が', 'P-k' ],
  [ '人間', 'N-nc' ],
  [ 'に', 'P-k' ],
  [ 'よっ', 'V-c' ],
  [ 'て', 'P-sj' ],
  [ '殺さ', 'V-c' ],
  [ 'れ', 'X' ],
  [ 'た', 'X' ],
  [ '。', 'M-p' ] ]
多く  名詞,副詞可能,*,*,*,*,多く,オオク,オーク
の     助詞,連体化,*,*,*,*,の,ノ,ノ
動物  名詞,一般,*,*,*,*,動物,ドウブツ,ドーブツ
が     助詞,格助詞,一般,*,*,*,が,ガ,ガ
人間  名詞,一般,*,*,*,*,人間,ニンゲン,ニンゲン
によって    助詞,格助詞,連語,*,*,*,によって,ニヨッテ,ニヨッテ
殺さ  動詞,自立,*,*,五段・サ行,未然形,殺す,コロサ,コロサ
れ     動詞,接尾,*,*,一段,連用形,れる,レ,レ
た     助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
。     記号,句点,*,*,*,*,。,。,。

5: 彼女は私たちの世話をしてくれる。
[ [ '彼女', 'D' ],
  [ 'は', 'P-rj' ],
  [ '私', 'D' ],
  [ 'たち', 'Q-n' ],
  [ 'の', 'P-k' ],
  [ '世話', 'N-nc' ],
  [ 'を', 'P-k' ],
  [ 'し', 'V-dp' ],
  [ 'て', 'P-sj' ],
  [ 'くれる', 'V-dp' ],
  [ '。', 'M-p' ] ]
彼女  名詞,代名詞,一般,*,*,*,彼女,カノジョ,カノジョ
は     助詞,係助詞,*,*,*,*,は,ハ,ワ
私たち   名詞,固有名詞,一般,*,*,*,私たち,ワタシタチ,ワタシタチ
の     助詞,連体化,*,*,*,*,の,ノ,ノ
世話  名詞,サ変接続,*,*,*,*,世話,セワ,セワ
を     助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
し     動詞,自立,*,*,サ変・スル,連用形,する,シ,シ
て     助詞,接続助詞,*,*,*,*,て,テ,テ
くれる   動詞,非自立,*,*,一段・クレル,基本形,くれる,クレル,クレル
。     記号,句点,*,*,*,*,。,。,。

確かに違いがありますが、 国語の専門家ではない僕にはどちらが正しい分割なのかよくわからないのが正直なところです(笑)

また mecab の「品詞IDの定義」で、"pos-id.def"の以下の部分に非常に興味が湧きました。

名詞,固有名詞,一般,* 41
名詞,固有名詞,人名,一般 42
名詞,固有名詞,人名,姓 43
名詞,固有名詞,人名,名 44
名詞,固有名詞,組織,* 45
名詞,固有名詞,地域,一般 46
名詞,固有名詞,地域,国 47

…というのも、次のブログで紹介されているように「固有表現」という概念があるそうです。

heat02zero.hatenablog.com

これを読むと mecab の品詞IDの固有名詞の区分は「人名」「地名」「組織名」「その他の固有表現」を意識したものではないかと考えました。

僕的には、そもそも ELIZA をベースにした対話システムの人間側の発話の形態素解析のために Rakuten MA に注目したこともあって、 その固有名詞が「人名」や「場所」、その他の様々な意味を示す言葉であることまでわかると 「ELIZA スクリプトが書きやすくなるなるだろうなぁ…」などと考えてます。

…ということで

今後は "learn-neologd.json" の方を使っていこうかと、考えています。

以上

*1:ご存知の方、教えてください。