PHPでMeCabのユーザー辞書を作成する

(1/1)
PHPとKAKASIを使って単語に分解する」では形態素解析ツール「KAKASI」を紹介したが、今回は、奈良先端科学技術大学院大学出身、現GoogleソフトウェアエンジニアでGoogle 日本語入力開発者の一人である工藤拓さんが開発している「MeCab」のユーザー辞書の作り方を説明する。
ユーザー辞書は、Wikipediaの見出し語をすべて取り込むことにする。

(2022年7月2日)コストを指定できるようにした.pahooInputData対応, 文脈ID見直し
(2022年5月7日)PHP8対応,リファラ・チェック改良.コマンドラインからの実行対応(CUI版)

目次

サンプル・プログラムの実行例

PHPでMeCabのユーザー辞書を作成する

サンプル・プログラム

圧縮ファイルの内容
makeudic_mecab.phpサンプル・プログラム本体
pahooInputData.phpデータ入力に関わる関数群。
使い方は「PHPでGET/POSTでフォームから値を受け取る」「数値入力とバリデーション」「文字入力とバリデーション」などを参照。include_path が通ったディレクトリに配置すること。
user_wiki.dicサンプル・プログラムを使って作った辞書ファイル(シフトJIS)
このプログラムを実行するには、MeCab がインストールされている必要がある。MeCab の入手方法やインストール方法については公式サイトを参照されたい。
MeCabChaSen をもとに開発が始まった。ChaSenに比べて解析精度は同程度で、解析速度は平均3~4倍速いという。

準備:各種定数

  46: //表示幅(ピクセル)
  47: define('WIDTH', 600);
  48: 
  49: //Spinner - jQuery UI を使用するかどうか
  50: define('USESPINNER', TRUE);
  51: 
  52: //コスト(初期値)
  53: define('COST_DEF', 10);
  54: //コスト(最小値)→コスト計算する
  55: define('COST_MIN', 0);
  56: //コスト(最大値)
  57: define('COST_MAX', 9999);
  58: 
  59: //ユーザー辞書
  60: define('FILE_UDIC_MECAB', 'user_wiki.dic');
  61: 
  62: //Wikipedia見出しファイル
  63: define('TITLES_WIKIPEDIA', 'https://download.wikimedia.org/jawiki/latest/jawiki-latest-all-titles-in-ns0.gz');
  64: 
  65: //Wikipedia見出しファイル保存ファイル名
  66: define('FILE_WIKIPEDIA', 'jawiki-latest-all-titles-in-ns0.gz');
  67: 
  68: //ユーザー辞書CSV
  69: define('FILE_WIKIPEDIA_CSV', 'user_wiki.csv');
  70: 
  71: //MeCab実行プログラム:各自の環境に合わせて
  72: define('MECAB', 'C:\Program Files\MeCab\bin\mecab.exe');
  73: 
  74: //MeCab辞書作成プログラム:各自の環境に合わせて
  75: define('MECABDICT', 'C:\Program Files\MeCab\bin\mecab-dict-index.exe');
  76: 
  77: //システム辞書:各自の環境に合わせて
  78: define('PATH_SYSDIC_MECAB', 'C:\Program Files\MeCab\dic\ipadic');

まず、各種パラメータを定数に設定する。

Wikipediaの見出し語は、"https://dumps.wikimedia.org/jawiki/latest/jawiki-latest-all-titles-in-ns0.gz" からダウンロードできる。これを定数 TITLES_WIKIPEDIA に格納する。

解説:ユーザー辞書作成

 241: /**
 242:  * ユーザー辞書を作成する
 243:  * @param   int    $cost   コスト
 244:  * @param   string $encode 文字コード
 245:  * @return  string 処理結果
 246: */
 247: function make_userdic($cost, $encode) {
 248:     //平均コストテーブル
 249:     static $table = array(
 250:         0,
 251:         7697, 7087, 7572, 7441, 7566, 7352, 6810, 6209, 5871, 5963,
 252:         5847, 5818, 5829, 5990, 5955, 5590, 5984, 5181, 5122, 5985,
 253:         5122, 5122, 5122, 4437, 5122
 254:     );
 255:     //追加辞書(漢数字無変換用)
 256:     static $add_dic1 = array('幾', '何');
 257:     static $add_dic2 = array('十', '百', '千');
 258:     static $add_dic3 = array('', '万', '億', '兆', '京');
 259: 
 260:     $msg = 'ユーザー辞書ファイルを作成しました.';
 261: 
 262:     $buff = file_get_contents(TITLES_WIKIPEDIA);
 263:     if ($buff == FALSEreturn 'エラー:Wikipediaタイトル・ファイルが見当たりません.';
 264:     $res = file_put_contents(FILE_WIKIPEDIA, $buff);
 265:     if ($res == FALSE)  return 'エラー:Wikipediaタイトル・ファイルをダウンロードできません.';
 266: 
 267:     $infp = gzopen(FILE_WIKIPEDIA, 'r');
 268:     if ($infp == FALSEreturn 'エラー:Wikipediaタイトル・ファイルを読み込めません.';
 269:     $outfp = fopen(FILE_WIKIPEDIA_CSV, 'w');
 270:     if ($infp == FALSEreturn 'エラー:ユーザー辞書ファイルを作成できません.';
 271: 
 272:     while (! feof($infp)) {
 273:         $str = trim(gzgets($infp));
 274:         if (preg_match('/[^ぁ-んァ-ヶー一-龠]/ui', $str) == 1)  continue;
 275: /** skip
 276:         if (preg_match('/^[0-9|a-z|\W_]/ui', $str) == 1)    continue;
 277:         if (preg_match('/_/ui', $str) == 1)                 continue;
 278:         if (preg_match('/曖昧さ回避/ui', $str) == 1)        continue;
 279:         if (preg_match('/一覧/ui', $str) == 1)              continue;
 280:         if (preg_match('/の登場人物/ui', $str) == 1)        continue;
 281: **/
 282:         if (mb_strlen($str<1)                            continue;
 283: 
 284:         //コスト計算
 285:         if ($cost <COST_MIN) {
 286:             $len = mb_strlen($str);
 287:             $cost = ($len > count($table- 1? 5000 : $table[$len];
 288:         }
 289:         $outstr = $str . ',1288,1288,' . $cost . ',名詞,固有名詞,*,*,*,*,' . $str . ",*,*,wikipedia\n";
 290:         if ($encode !INTERNAL_ENCODING) {
 291:             $outstr = mb_convert_encoding($outstr, $encode, INTERNAL_ENCODING);
 292:         }
 293:         $res = fwrite($outfp, $outstr);
 294:         if ($res == FALSE) {
 295:             $msg = 'エラー:ユーザー辞書ファイルを作成できません.';
 296:             break;
 297:         }
 298:     }
 299: 
 300:     //追加辞書作成
 301:     $error = FALSE;
 302:     foreach ($add_dic1 as $ss1) {
 303:         if ($error)     break;
 304:         foreach ($add_dic2 as $ss2) {
 305:             if ($error)     break;
 306:             foreach ($add_dic3 as $ss3) {
 307:                 $str = $ss1 . $ss2 . $ss3;
 308:                 $cost = 10;     //コスト固定
 309:                 $outstr = $str . ',1288,1288,' . $cost . ',名詞,固有名詞,*,*,*,*,' . $str . ",*,*,wikipedia\n";
 310:                 if ($encode !INTERNAL_ENCODING) {
 311:                     $outstr = mb_convert_encoding($outstr, $encode, INTERNAL_ENCODING);
 312:                 }
 313:                 $res = fwrite($outfp, $outstr);
 314:                 if ($res == FALSE) {
 315:                     $msg = 'エラー:ユーザー辞書ファイルを作成できません.';
 316:                     $error = TRUE;
 317:                     break;
 318:                 }
 319:             }
 320:         }
 321:     }
 322: 
 323:     fclose($outfp);
 324:     gzclose($infp);
 325: 
 326:     $cmd = '"' . MECABDICT . '" -d "' . PATH_SYSDIC_MECAB . '" -u "' . FILE_UDIC_MECAB .'" -f ' . $encode .' -t ' . $encode . ' ' . FILE_WIKIPEDIA_CSV;
 327:  exec($cmd);
 328: 
 329: //  unlink(FILE_WIKIPEDIA);
 330: //  unlink(FILE_WIKIPEDIA_CSV);
 331: 
 332:     return $msg;
 333: }

TITLES_WIKIPEDIA は圧縮ファイルのため、組み込み関数  gzgets  を使って読み込む。だが、 gzopen  はローカルドライブにしか対応していないので、 file_get_contents  と  file_put_contents  を使ってローカルドライブにコピーする。

 gzgets  を使って1行ずつ読み込み、ユーザー辞書作成のためのCSV形式に変換して FILE_WIKIPEDIA_CSV に出力する。
なお、TITLES_WIKIPEDIA には辞書として登録する必要のない見出しもあるので、これらは  preg_match  によって弾いている。

CSV形式のフォーマットは以下の通り。
表層形,左文脈ID,右文脈ID,コスト,品詞,品詞細分類1,品詞細分類2,品詞細分類3,活用形,活用型,原形,読み,発音
ここでは、左文脈IDと右文脈IDは固有名詞1288として固定にした。

コストは、その単語がどれだけ出現しやすいかを示している。小さいほど出現しやすいという意味になる。
ここでは、単語の文字列長に応じてコストを決定するテーブル $table を用意した。
このテーブルの値は、MeCabに標準で用意されているシステム辞書のコストの平均値を計算したものである。具体的には、'MeCab/dic/ipadic' に格納されているCSVファイルから、文字列長とコストをとりだし、文字列長毎のコストの平均を計算した。
このアイデアについては、「wikipediaのデータや顔文字辞書からmecabのユーザ辞書を作成するフレームワーク」を参考にした。
ただし、変数 $cost が指定されている場合は、その値に合わせて固定値とした。辞書ファイルを使うアプリケーション(例:PHPで日本語テキストを正規化)によっては、この辞書の固有名詞を優先させた方が良い結果を得られるので。

CSVファイル FILE_WIKIPEDIA_CSV が作成できたら、ここからMeCabの辞書作成ツール "mecab-dict-index"(定数 MECABDICT に定義)を使って辞書ファイルを作成する。

参考サイト

(この項おわり)
header