MeCab 辞書を未知語の英数字が分割されないようにカスタマイズする

# はじめに

突然ですが、次の文章を [MeCab](https://taku910.github.io/mecab/) で形態素解析してみましょう。

```
eneloop proの単3形(4本入)の型番はBK-3HCD/4Hです。
```

```console
$ echo "eneloop proの単3形(4本入)の型番はBK-3HCD/4Hです。" | mecab                      
eneloop 名詞,固有名詞,組織,*,*,*,*
pro     名詞,一般,*,*,*,*,*
の      助詞,連体化,*,*,*,*,の,ノ,ノ
単      接頭詞,名詞接続,*,*,*,*,単,タン,タン
3       名詞,数,*,*,*,*,*
形      名詞,一般,*,*,*,*,形,カタチ,カタチ
(       名詞,サ変接続,*,*,*,*,*
4       名詞,数,*,*,*,*,*
本      名詞,接尾,助数詞,*,*,*,本,ホン,ホン
入      接頭詞,名詞接続,*,*,*,*,入,ニュウ,ニュー
)       名詞,サ変接続,*,*,*,*,*
の      助詞,連体化,*,*,*,*,の,ノ,ノ
型番    名詞,一般,*,*,*,*,型番,カタバン,カタバン
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
BK      名詞,固有名詞,組織,*,*,*,*
-       名詞,サ変接続,*,*,*,*,*
3       名詞,数,*,*,*,*,*
HCD     名詞,一般,*,*,*,*,*
/       名詞,サ変接続,*,*,*,*,*
4       名詞,数,*,*,*,*,*
H       名詞,一般,*,*,*,*,*
です    助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
。      記号,句点,*,*,*,*,。,。,。
EOS
```

型番部分である `BK-3HCD/4H` はもちろん辞書に登録されていない未知語なので、以下のような細切れなトークンに分割されてしまいました。

```
BK      名詞,固有名詞,組織,*,*,*,*
-       名詞,サ変接続,*,*,*,*,*
3       名詞,数,*,*,*,*,*
HCD     名詞,一般,*,*,*,*,*
/       名詞,サ変接続,*,*,*,*,*
4       名詞,数,*,*,*,*,*
H       名詞,一般,*,*,*,*,*
```

オークファンのシステムで処理しなければいけない文字列には型番が含まれていることがあるので、この形態素解析結果はかなり問題です。

そこでこの記事では、未知語の英数字 (と一部の記号) が分割されてしまわないように MeCab の辞書をカスタマイズする方法をご紹介しようと思います。

作業は macOS または Ubuntu (Windows の WSL 上の Ubuntu でも OK) で実施する想定とします。

# 辞書の準備

## IPA 辞書の取得

MeCab のデフォルトの辞書は IPA 辞書なので、[MeCab のページのダウンロード](https://taku910.github.io/mecab/#download) から [mecab-ipadic-2.7.0-20070801.tar.gz](https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7MWVlSDBCSXZMTXM) を取得しておきます。

ここでは `seed` ディレクトリを作成し、そこに辞書データを展開します。

```console
$ mkdir ./seed
$ mv ~/Downloads/mecab-ipadic-2.7.0-20070801.tar.gz ./seed/
$ tar -zxvf ./seed/mecab-ipadic-2.7.0-20070801.tar.gz -C ./seed/
```

展開されたデータから配布用のバイナリ辞書を作成するのに必要なファイルのみ `seed/mecab-ipadic` ディレクトリにコピーします。具体的には Seed 辞書 (`*.csv`)、定義ファイル (`*.def`)、設定ファイル (`dicrc`) をコピーします。

```console
$ mkdir ./seed/mecab-ipadic
$ cp ./seed/mecab-ipadic-2.7.0-20070801/*.{csv,def} ./seed/mecab-ipadic/
$ cp ./seed/mecab-ipadic-2.7.0-20070801/dicrc ./seed/mecab-ipadic/
```

## 文字コードの変換

ここで、ダウンロードした IPA 辞書の Seed 辞書と定義ファイルの文字コードが EUC-JP なので、今後のカスタマイズをしやすくするために UTF-8 に一括変換しておきます。

`nkf` コマンドを使用するのでインストールしておきます。

__macOS の場合__

```console
$ brew install nkf
```

__Ubuntu の場合__

```console
$ sudo apt install nkf
```

文字コードを UTF-8 に一括変換します。(改行コードも LF に統一しています。)

```console
$ find ./seed/mecab-ipadic -type f -name "*.*" -exec nkf -w -Lu --overwrite {} \;
```

設定ファイル `dicrc` を以下のように変更しておきます。

```ini
;
; Configuration file of IPADIC
;
; $Id: dicrc,v 1.4 2006/04/08 06:41:36 taku-ku Exp $;
;
cost-factor = 800
bos-feature = BOS/EOS,*,*,*,*,*,*,*,*
eval-size = 8
unk-eval-size = 4
; 以下を EUC-JP から UTF-8 に変更
config-charset = UTF-8

; (後略)
```

## IPA 辞書のコンパイル

まず、何もカスタマイズしていない IPA 辞書をコンパイルして配布用バイナリ辞書を `dic/mecab-ipadic` に作成してみます。

出力用のディレクトリを作成しておきます。

```console
$ mkdir -p ./dic/mecab-ipadic
```

MeCab をインストールして辞書をコンパイルします。

__macOS の場合__

```console
$ brew install mecab
$ /opt/homebrew/Cellar/mecab/0.996/libexec/mecab/mecab-dict-index -f utf-8 -t utf-8 -d ./seed/mecab-ipadic -o ./dic/mecab-ipadic
```

__Ubuntu の場合__

```console
$ sudo apt install mecab libmecab-dev
$ /usr/lib/mecab/mecab-dict-index -f utf-8 -t utf-8 -d ./seed/mecab-ipadic -o ./dic/mecab-ipadic
```

配布用バイナリ辞書にも設定ファイルの `dicrc` が必要なのでコピーしておきます。

```console
$ cp ./seed/mecab-ipadic/dicrc ./dic/mecab-ipadic/
```

作成された配布用バイナリ辞書を確認しておきます。

```console
$ mecab -D -d ./dic/mecab-ipadic                      
filename:       ./dic/mecab-ipadic/sys.dic
version:        102
charset:        utf-8
type:   0
size:   392126
left size:      1316
right size:     1316
```

カスタマイズ前の形態素解析結果も確認しておきます。

```console
$ echo "eneloop proの単3形(4本入)の型番はBK-3HCD/4Hです。" | mecab -d ./dic/mecab-ipadic
eneloop 名詞,固有名詞,組織,*,*,*,*
pro     名詞,一般,*,*,*,*,*
の      助詞,連体化,*,*,*,*,の,ノ,ノ
単      接頭詞,名詞接続,*,*,*,*,単,タン,タン
3       名詞,数,*,*,*,*,*
形      名詞,一般,*,*,*,*,形,カタチ,カタチ
(       名詞,サ変接続,*,*,*,*,*
4       名詞,数,*,*,*,*,*
本      名詞,接尾,助数詞,*,*,*,本,ホン,ホン
入      接頭詞,名詞接続,*,*,*,*,入,ニュウ,ニュー
)       名詞,サ変接続,*,*,*,*,*
の      助詞,連体化,*,*,*,*,の,ノ,ノ
型番    名詞,一般,*,*,*,*,型番,カタバン,カタバン
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
BK      名詞,固有名詞,組織,*,*,*,*
-       名詞,サ変接続,*,*,*,*,*
3       名詞,数,*,*,*,*,*
HCD     名詞,一般,*,*,*,*,*
/       名詞,サ変接続,*,*,*,*,*
4       名詞,数,*,*,*,*,*
H       名詞,一般,*,*,*,*,*
です    助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
。      記号,句点,*,*,*,*,。,。,。
EOS
```

# 辞書のカスタマイズ

辞書の準備ができたのでカスタマイズしていきます。ここでは以下の文字が連続する部分がひとかたまりのトークンとなるようにカスタマイズしてみます。

* 大文字の英字
* 小文字の英字
* 数字
* `-`
* `/`

英数字対応の Seed 辞書を格納する `seed/mecab-ipadic-alphanumeric` ディレクトリを作成し、元データをコピーします。

```console
$ cp -r ./seed/mecab-ipadic ./seed/mecab-ipadic-alphanumeric
```

未知語処理の定義ファイル `char.def` と未知語用の辞書 `unk.def` をカスタマイズしていきます。

## 未知語処理定義のカスタマイズ

`seed/mecab-ipadic-alphanumeric/char.def` はもともと以下のように定義されています。

```conf
# (前略)

DEFAULT	       0 1 0  # DEFAULT is a mandatory category!
SPACE	       0 1 0
KANJI	       0 0 2
SYMBOL	       1 1 0
NUMERIC	       1 1 0
ALPHA	       1 1 0
HIRAGANA       0 1 2
KATAKANA       1 1 2
KANJINUMERIC   1 1 0
GREEK	       1 1 0
CYRILLIC       1 1 0

# (中略)

# ASCII
# ! から /
0x0021..0x002F SYMBOL
# 0 から 9
0x0030..0x0039 NUMERIC
# : から @
0x003A..0x0040 SYMBOL
# A から Z
0x0041..0x005A ALPHA
# [ から `
0x005B..0x0060 SYMBOL
# a から z
0x0061..0x007A ALPHA
# { から ~
0x007B..0x007E SYMBOL

# (後略)
```

16 進数と ASCII 文字の対応は以下の表のようになっています。

| 16 進数 | 文字 | 16 進数 | 文字 | 16 進数 | 文字 | 16 進数 | 文字 |
|---:|:---|---:|:---|---:|:---|---:|:---|
| 0x0000 | NUL | 0x0010 | DLE | 0x0020 | SP | 0x0030 | 0 |
| 0x0001 | SOH | 0x0011 | DC1 | 0x0021 | ! | 0x0031 | 1 |
| 0x0002 | STX | 0x0012 | DC2 | 0x0022 | " | 0x0032 | 2 |
| 0x0003 | ETX | 0x0013 | DC3 | 0x0023 | # | 0x0033 | 3 |
| 0x0004 | EOT | 0x0014 | DC4 | 0x0024 | $ | 0x0034 | 4 |
| 0x0005 | ENQ | 0x0015 | NAK | 0x0025 | % | 0x0035 | 5 |
| 0x0006 | ACK | 0x0016 | SYN | 0x0026 | & | 0x0036 | 6 |
| 0x0007 | BEL | 0x0017 | ETB | 0x0027 | ' | 0x0037 | 7 |
| 0x0008 | BS | 0x0018 | CAN | 0x0028 | ( | 0x0038 | 8 |
| 0x0009 | HT | 0x0019 | EM | 0x0029 | ) | 0x0039 | 9 |
| 0x000A | LF | 0x001A | SUB | 0x002A | * | 0x003A | : |
| 0x000B | VT | 0x001B | ESC | 0x002B | + | 0x003B | ; |
| 0x000C | FF | 0x001C | FS | 0x002C | , | 0x003C | < |
| 0x000D | CR | 0x001D | GS | 0x002D | - | 0x003D | = |
| 0x000E | SO | 0x001E | RS | 0x002E | . | 0x003E | > |
| 0x000F | SI | 0x001F | US | 0x002F | / | 0x003F | ? |

| 16 進数 | 文字 | 16 進数 | 文字 | 16 進数 | 文字 | 16 進数 | 文字 |
|---:|:---|---:|:---|---:|:---|---:|:---|
| 0x0040 | @ | 0x0050 | P | 0x0060 | ` | 0x0070 | p |
| 0x0041 | A | 0x0051 | Q | 0x0061 | a | 0x0071 | q |
| 0x0042 | B | 0x0052 | R | 0x0062 | b | 0x0072 | r |
| 0x0043 | C | 0x0053 | S | 0x0063 | c | 0x0073 | s |
| 0x0044 | D | 0x0054 | T | 0x0064 | d | 0x0074 | t |
| 0x0045 | E | 0x0055 | U | 0x0065 | e | 0x0075 | u |
| 0x0046 | F | 0x0056 | V | 0x0066 | f | 0x0076 | v |
| 0x0047 | G | 0x0057 | W | 0x0067 | g | 0x0077 | w |
| 0x0048 | H | 0x0058 | X | 0x0068 | h | 0x0078 | x |
| 0x0049 | I | 0x0059 | Y | 0x0069 | i | 0x0079 | y |
| 0x004A | J | 0x005A | Z | 0x006A | j | 0x007A | z |
| 0x004B | K | 0x005B | [ | 0x006B | k | 0x007B | { |
| 0x004C | L | 0x005C | ¥ | 0x006C | l | 0x007C | \| |
| 0x004D | M | 0x005D | ] | 0x006D | m | 0x007D | } |
| 0x004E | N | 0x005E | ^ | 0x006E | n | 0x007E | ~ |
| 0x004F | O | 0x005F | _ | 0x006F | o | 0x007F | DEL |

上記の ASCII コード表を確認しながら `seed/mecab-ipadic-alphanumeric/char.def` を以下のように変更します。

```conf
# (前略)

DEFAULT	       0 1 0  # DEFAULT is a mandatory category!
SPACE	       0 1 0
KANJI	       0 0 2
SYMBOL	       1 1 0
NUMERIC	       1 1 0
ALPHA	       1 1 0
# 英数字の字種を追加
ALPHANUMERIC   1 1 0
HIRAGANA       0 1 2
KATAKANA       1 1 2
KANJINUMERIC   1 1 0
GREEK	       1 1 0
CYRILLIC       1 1 0

# (中略)

# ASCII
# ! から ,
0x0021..0x002C SYMBOL
# -
0x002D         SYMBOL ALPHANUMERIC
# .
0x002E         SYMBOL
# /
0x002F         SYMBOL ALPHANUMERIC
# 0 から 9
0x0030..0x0039 NUMERIC ALPHANUMERIC
# : から @
0x003A..0x0040 SYMBOL
# A から Z
0x0041..0x005A ALPHANUMERIC
# [ から `
0x005B..0x0060 SYMBOL
# a から z
0x0061..0x007A ALPHANUMERIC
# { から ~
0x007B..0x007E SYMBOL

# (後略)
```

## 未知語用辞書のカスタマイズ

次に `seed/mecab-ipadic-alphanumeric/unk.def` に英数字の字種の定義を以下のように追記します。また、ここでは記号の字種 `SYMBOL` の品詞の `名詞,サ変接続` から `記号,一般` への変更も実施しています。(`unk.def` はコメントの記述に対応していないようなので、下記の `#` の行は削除してください。)

```csv
DEFAULT,5,5,4769,記号,一般,*,*,*,*,*
SPACE,9,9,8903,記号,空白,*,*,*,*,*
KANJI,1285,1285,11426,名詞,一般,*,*,*,*,*
KANJI,1283,1283,17290,名詞,サ変接続,*,*,*,*,*
KANJI,1293,1293,17611,名詞,固有名詞,地域,一般,*,*,*
KANJI,1292,1292,12649,名詞,固有名詞,組織,*,*,*,*
KANJI,1289,1289,17340,名詞,固有名詞,人名,一般,*,*,*
KANJI,1288,1288,15295,名詞,固有名詞,一般,*,*,*,*
# 英数字の字種の定義を追加
ALPHANUMERIC,1204,1204,0,名詞,英数字,*,*,*,*,*
# 記号の字種の品詞を `名詞,サ変接続` から `記号,一般` に変更
SYMBOL,1283,1283,17585,記号,一般,*,*,*,*,*
NUMERIC,1295,1295,27386,名詞,数,*,*,*,*,*
ALPHA,1285,1285,13398,名詞,一般,*,*,*,*,*
ALPHA,1293,1293,18706,名詞,固有名詞,地域,一般,*,*,*
ALPHA,1292,1292,13835,名詞,固有名詞,組織,*,*,*,*
ALPHA,1289,1289,18188,名詞,固有名詞,人名,一般,*,*,*
ALPHA,1288,1288,15673,名詞,固有名詞,一般,*,*,*,*
ALPHA,3,3,15235,感動詞,*,*,*,*,*,*
HIRAGANA,1285,1285,13069,名詞,一般,*,*,*,*,*
HIRAGANA,1283,1283,20223,名詞,サ変接続,*,*,*,*,*
# (後略)
```

## 英数字対応辞書のコンパイル

英数字対応の配布用バイナリ辞書を出力するディレクトリを作成しておきます。

```console
$ mkdir ./dic/mecab-ipadic-alphanumeric
```

カスタマイズした辞書をコンパイルします。

__macOS の場合__

```console
$ /opt/homebrew/Cellar/mecab/0.996/libexec/mecab/mecab-dict-index -f utf-8 -t utf-8 -d ./seed/mecab-ipadic-alphanumeric -o ./dic/mecab-ipadic-alphanumeric
```

__Ubuntu の場合__

```console
$ /usr/lib/mecab/mecab-dict-index -f utf-8 -t utf-8 -d ./seed/mecab-ipadic-alphanumeric -o ./dic/mecab-ipadic-alphanumeric
```

設定ファイル `dicrc` をコピーします。

```console
$ cp ./seed/mecab-ipadic-alphanumeric/dicrc ./dic/mecab-ipadic-alphanumeric/
```

作成された配布用バイナリ辞書を確認しておきます。

```console
$ mecab -D -d ./dic/mecab-ipadic-alphanumeric                                   
filename:       ./dic/mecab-ipadic-alphanumeric/sys.dic
version:        102
charset:        utf-8
type:   0
size:   392126
left size:      1316
right size:     1316
```

カスタマイズ後の形態素解析結果を確認しておきます。

```console
$ echo "eneloop proの単3形(4本入)の型番はBK-3HCD/4Hです。" | mecab -d ./dic/mecab-ipadic-alphanumeric
eneloop 名詞,英数字,*,*,*,*,*
pro     名詞,英数字,*,*,*,*,*
の      助詞,連体化,*,*,*,*,の,ノ,ノ
単      接頭詞,名詞接続,*,*,*,*,単,タン,タン
3       名詞,数,*,*,*,*,*
形      名詞,一般,*,*,*,*,形,カタチ,カタチ
(       記号,一般,*,*,*,*,*
4       名詞,数,*,*,*,*,*
本      名詞,接尾,助数詞,*,*,*,本,ホン,ホン
入      接頭詞,名詞接続,*,*,*,*,入,ニュウ,ニュー
)       記号,一般,*,*,*,*,*
の      助詞,連体化,*,*,*,*,の,ノ,ノ
型番    名詞,一般,*,*,*,*,型番,カタバン,カタバン
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
BK-3HCD/4H      名詞,英数字,*,*,*,*,*
です    助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
。      記号,句点,*,*,*,*,。,。,。
EOS
```

型番部分の `BK-3HCD/4H` が分割されずに 1 つのトークンにまとまるようになりました。

```
BK-3HCD/4H      名詞,英数字,*,*,*,*,*
```

# おまけ

ここで、おやっ？ と思ったそこのあなた...
気づいてしまいましたね :innocent:

カスタマイズ前の未知語処理の結果

```
eneloop 名詞,固有名詞,組織,*,*,*,*
pro     名詞,一般,*,*,*,*,*
```

がカスタマイズ後は

```
eneloop 名詞,英数字,*,*,*,*,*
pro     名詞,英数字,*,*,*,*,*
```

と雑な感じに劣化しています。

これは `unk.def` で定義されていた `ALPHA` の字種が担当していた部分をすべて `ALPHANUMERIC` に置き換えてしまったのが原因です。

```csv
# (前略)
ALPHANUMERIC,1204,1204,0,名詞,英数字,*,*,*,*,*
SYMBOL,1283,1283,17585,記号,一般,*,*,*,*,*
NUMERIC,1295,1295,27386,名詞,数,*,*,*,*,*
ALPHA,1285,1285,13398,名詞,一般,*,*,*,*,*
ALPHA,1293,1293,18706,名詞,固有名詞,地域,一般,*,*,*
ALPHA,1292,1292,13835,名詞,固有名詞,組織,*,*,*,*
ALPHA,1289,1289,18188,名詞,固有名詞,人名,一般,*,*,*
ALPHA,1288,1288,15673,名詞,固有名詞,一般,*,*,*,*
ALPHA,3,3,15235,感動詞,*,*,*,*,*,*
# (後略)
```

MeCab の用途が文字列をトークンに分割するだけであれば、このままでもさほど問題ではないのですが、少しだけ対策を実施してみます。`ALPHANUMERIC` を `ALPHA` と同じように調整してもよいですが、ここではユーザー辞書への登録で改善してみようと思います。

まず、`seed/mecab-ipadic-alphanumeric/user.csv` (拡張子が `.csv` であればファイル名は任意で OK) を以下の内容 (文字コードは UTF-8) で作成します。

```csv
eneloop,1223,1223,6058,名詞,固有名詞,ブランド,*,*,*,eneloop,エネループ,エネループ
pro,1223,1223,6058,名詞,一般,,*,*,*,PRO,プロ,プロ
```

次に、`seed/mecab-ipadic-alphanumeric/char.def` に登録されている `ALPHANUMERIC` の定義を

```conf
ALPHANUMERIC   1 1 0
```

から

```conf
ALPHANUMERIC   0 1 0
```

に変更して、既知語として辞書に登録されている場合は未知語処理を動作させないようにします。

準備ができたので辞書をコンパイルしなおします。

__macOS の場合__

```console
$ /opt/homebrew/Cellar/mecab/0.996/libexec/mecab/mecab-dict-index -f utf-8 -t utf-8 -d ./seed/mecab-ipadic-alphanumeric -o ./dic/mecab-ipadic-alphanumeric
```

__Ubuntu の場合__

```console
$ /usr/lib/mecab/mecab-dict-index -f utf-8 -t utf-8 -d ./seed/mecab-ipadic-alphanumeric -o ./dic/mecab-ipadic-alphanumeric
```

形態素解析の結果を確認してみましょう。

```console
$ echo "eneloop proの単3形(4本入)の型番はBK-3HCD/4Hです。" | mecab -d ./dic/mecab-ipadic-alphanumeric
eneloop 名詞,固有名詞,ブランド,*,*,*,eneloop,エネループ,エネループ
pro     名詞,一般,,*,*,*,PRO,プロ,プロ
の      助詞,連体化,*,*,*,*,の,ノ,ノ
単      接頭詞,名詞接続,*,*,*,*,単,タン,タン
3       名詞,数,*,*,*,*,*
形      名詞,一般,*,*,*,*,形,カタチ,カタチ
(       記号,一般,*,*,*,*,*
4       名詞,数,*,*,*,*,*
本      名詞,接尾,助数詞,*,*,*,本,ホン,ホン
入      接頭詞,名詞接続,*,*,*,*,入,ニュウ,ニュー
)       記号,一般,*,*,*,*,*
の      助詞,連体化,*,*,*,*,の,ノ,ノ
型番    名詞,一般,*,*,*,*,型番,カタバン,カタバン
は      助詞,係助詞,*,*,*,*,は,ハ,ワ
BK-3HCD/4H      名詞,英数字,*,*,*,*,*
です    助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
。      記号,句点,*,*,*,*,。,。,。
EOS
```

`eneloop` と `pro` は未知語ではなく既知語になりました。

```
eneloop 名詞,固有名詞,ブランド,*,*,*,eneloop,エネループ,エネループ
pro     名詞,一般,,*,*,*,PRO,プロ,プロ
```

# おわりに

この記事では MeCab の辞書をカスタマイズする一例として、未知語の英数字 (と一部の記号) の連続を 1 トークンにまとめる方法をご紹介しました。今回の例では IPA 辞書をカスタマイズしましたが、もちろん他の辞書でもカスタマイズは可能です。実際にオークファンのシステムでは [mecab-ipadic-NEologd](https://github.com/neologd/mecab-ipadic-neologd/blob/master/README.ja.md) をカスタマイズして使用しています。

この他にも [MeCab の公式ドキュメント](https://taku910.github.io/mecab/) には様々なカスタマイズ方法が紹介されています。記事では説明しなかったカスタマイズ内容の詳細は以下に記載されていますので、細かい設定値など気になる方は確認してみてください。おもしろいカスタマイズ方法を思いつくかもしれません :thumbsup: 

* [未知語処理の定義](https://taku910.github.io/mecab/unk.html)
* [オリジナル辞書/コーパスからのパラメータ推定](https://taku910.github.io/mecab/learn.html)
* [単語の追加方法](https://taku910.github.io/mecab/dic.html)