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

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

(2022年5月7日)PHP8対応,リファラ・チェック改良.コマンドラインからの実行対応(CUI版)

目次

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

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

サンプル・プログラム

圧縮ファイルの内容
makeudic_mecab.phpサンプル・プログラム本体
このプログラムを実行するには、MeCab がインストールされている必要がある。MeCab の入手方法やインストール方法については公式サイトを参照されたい。
MeCabChaSen をもとに開発が始まった。ChaSenに比べて解析精度は同程度で、解析速度は平均3~4倍速いという。
ユーザー関数 isCommandLineisButtongetParam については、「PHPでGET/POSTでフォームから値を受け取る」をご覧いただきたい。

準備:各種定数

0045: //ユーザー辞書
0046: define('FILE_UDIC_MECAB', 'user_wiki.dic');
0047: 
0048: //Wikipedia見出しファイル
0049: define('TITLES_WIKIPEDIA', 'https://download.wikimedia.org/jawiki/latest/jawiki-latest-all-titles-in-ns0.gz');
0050: 
0051: //Wikipedia見出しファイル保存ファイル名
0052: define('FILE_WIKIPEDIA', 'jawiki-latest-all-titles-in-ns0.gz');
0053: 
0054: //ユーザー辞書CSV
0055: define('FILE_WIKIPEDIA_CSV', 'user_wiki.csv');
0056: 
0057: //MeCab実行プログラム:各自の環境に合わせて
0058: define('MECAB', 'C:\Program Files (x86)\MeCab\bin\mecab.exe');
0059: 
0060: //MeCab辞書作成プログラム:各自の環境に合わせて
0061: define('MECABDICT', 'C:\Program Files (x86)\MeCab\bin\mecab-dict-index.exe');
0062: 
0063: //システム辞書:各自の環境に合わせて
0064: define('PATH_SYSDIC_MECAB', 'C:\Program Files (x86)\MeCab\dic\ipadic');

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

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

解説:ユーザー辞書作成

0355: /**
0356:  * ユーザー辞書を作成する
0357:  * @param   string $encode 文字コード
0358:  * @return  string 処理結果
0359: */
0360: function make_userdic($encode) {
0361:     //平均コストテーブル
0362:     static $table = array(
0363:         0,
0364:         7697, 7087, 7572, 7441, 7566, 7352, 6810, 6209, 5871, 5963,
0365:         5847, 5818, 5829, 5990, 5955, 5590, 5984, 5181, 5122, 5985,
0366:         5122, 5122, 5122, 4437, 5122
0367:     );
0368:     //追加辞書(漢数字無変換用)
0369:     static $add_dic1 = array('', '');
0370:     static $add_dic2 = array('', '', '');
0371:     static $add_dic3 = array('', '', '', '', '');
0372: 
0373:     $msg = 'ユーザー辞書ファイルを作成しました.';
0374: 
0375:     $buff = file_get_contents(TITLES_WIKIPEDIA);
0376:     if ($buff == FALSEreturn 'エラー:Wikipediaタイトル・ファイルが見当たりません.';
0377:     $res = file_put_contents(FILE_WIKIPEDIA$buff);
0378:     if ($res == FALSE)  return 'エラー:Wikipediaタイトル・ファイルをダウンロードできません.';
0379: 
0380:     $infp = gzopen(FILE_WIKIPEDIA, 'r');
0381:     if ($infp == FALSEreturn 'エラー:Wikipediaタイトル・ファイルを読み込めません.';
0382:     $outfp = fopen(FILE_WIKIPEDIA_CSV, 'w');
0383:     if ($infp == FALSEreturn 'エラー:ユーザー辞書ファイルを作成できません.';
0384: 
0385:     while (! feof($infp)) {
0386:         $str = trim(gzgets($infp));
0387:         if (preg_match('/[^ぁ-んァ-ヶー一-龠]/ui', $str) == 1)    continue;
0388: /**
0389:         if (preg_match('/^[0-9|a-z|\W_]/ui', $str) == 1)   continue;
0390:         if (preg_match('/_/ui', $str) == 1)                    continue;
0391:         if (preg_match('/曖昧さ回避/ui', $str) == 1)        continue;
0392:         if (preg_match('/一覧/ui', $str) == 1)               continue;
0393:         if (preg_match('/の登場人物/ui', $str) == 1)        continue;
0394: **/
0395:         if (mb_strlen($str) <= 1)                           continue;
0396: 
0397:         //コスト計算
0398:         $len = mb_strlen($str);
0399:         $cost = ($len > count($table) - 1) ? 5000 : $table[$len];
0400:         $outstr = $str . ',128,128,' . $cost . ',名詞,固有名詞,*,*,*,*,' . $str . ",*,*,wikipedia\n";
0401:         if ($encode != INTERNAL_ENCODING) {
0402:             $outstr = mb_convert_encoding($outstr$encodeINTERNAL_ENCODING);
0403:         }
0404:         $res = fwrite($outfp$outstr);
0405:         if ($res == FALSE) {
0406:             $msg = 'エラー:ユーザー辞書ファイルを作成できません.';
0407:             break;
0408:         }
0409:     }
0410: 
0411:     //追加辞書作成
0412:     $error = FALSE;
0413:     foreach ($add_dic1 as $ss1) {
0414:         if ($error)     break;
0415:         foreach ($add_dic2 as $ss2) {
0416:             if ($error)     break;
0417:             foreach ($add_dic3 as $ss3) {
0418:                 $str = $ss1 . $ss2 . $ss3;
0419:                 $cost = 100;     //コスト固定
0420:                 $outstr = $str . ',128,128,' . $cost . ',名詞,固有名詞,*,*,*,*,' . $str . ",*,*,wikipedia\n";
0421:                 if ($encode != INTERNAL_ENCODING) {
0422:                     $outstr = mb_convert_encoding($outstr$encodeINTERNAL_ENCODING);
0423:                 }
0424:                 $res = fwrite($outfp$outstr);
0425:                 if ($res == FALSE) {
0426:                     $msg = 'エラー:ユーザー辞書ファイルを作成できません.';
0427:                     $error = TRUE;
0428:                     break;
0429:                 }
0430:             }
0431:         }
0432:     }
0433: 
0434:     fclose($outfp);
0435:     gzclose($infp);
0436: 
0437:     $cmd = '"' . MECABDICT . '" -d "' . PATH_SYSDIC_MECAB . '" -u "' . FILE_UDIC_MECAB .'" -f ' . $encode .' -t ' . $encode . ' ' . FILE_WIKIPEDIA_CSV;
0438:     exec($cmd);
0439: 
0440: //  unlink(FILE_WIKIPEDIA);
0441: //  unlink(FILE_WIKIPEDIA_CSV);
0442: 
0443:     return $msg;
0444: }

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は128で固定にした。

コストは、その単語がどれだけ出現しやすいかを示している。小さいほど出現しやすいという意味になる。
ここでは、単語の文字列長に応じてコストを決定するテーブル $table を用意した。
このテーブルの値は、MeCabに標準で用意されているシステム辞書のコストの平均値を計算したものである。具体的には、'MeCab/dic/ipadic' に格納されているCSVファイルから、文字列長とコストをとりだし、文字列長毎のコストの平均を計算した。
このアイデアについては、「wikipediaのデータや顔文字辞書からmecabのユーザ辞書を作成するフレームワーク」を参考にした。

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

参考サイト

(この項おわり)
header