中国語をCEDICT, MeCab, Kuromojiで単語分割する

広告

形態素解析ではありません。品詞などの情報は意識せず、長い単語を優先してザクザク切っていくだけです。

CC-CEDICTはオープンソース(Creative Commons)の中国語の辞書です。繁体字、簡体字、ピンインの発音に加えて、英語の意味がついています。品詞がついていればよかった。

まず、CEDICTのデータを雑に処理します。

MeCab辞書を作る

csvファイル

言語は何でもいいのですが、Rubyで簡単なプログラムを書きました。

cedict = open('cedict_ts.u8')
cedict.each do |line|
    line.chomp!

    if line =~ /^(.*?)\s(.*?)\s\[(.*?)\]/
        traditional = $1
        simplified = $2
        pinyin = $3

        # カンマで区切られている単語は長いので切ることにする
        traditional = traditional.split(/,/)
        simplified = simplified.split(/,/)
        pinyin = pinyin.split(/,/)

        size = simplified.size
        (0..size-1).each {|i|

            cost = 0

            # ピンインに大文字が含まれる語は固有名詞らしいので選ばれやすくする(コストが小さくなるようにする)
            if pinyin[i] =~ /[A-Z]/ then
                cost = [-32767, (-400 * (simplified[i].length ** 2)).to_i].max
            else
                cost = [-32767, (-400 * (simplified[i].length ** 1.5)).to_i].max
            end

            s = simplified[i].downcase.strip
            t = traditional[i].downcase.strip

            # 簡体字・繁体字の両方を出力する
            puts "#{s},0,0,#{cost},*,*,*,*,*,*,#{t},#{s},#{pinyin[i]}"
            puts "#{t},0,0,#{cost},*,*,*,*,*,*,#{t},#{s},#{pinyin[i]}"
        }
    end
end

コストは小さい値が優先して採用されますので、長さの1.5乗に適当なマイナスの値を乗じて生成します。素性は特にありませんが、IPADICと同じ数に合わせておきます(そうしないとKuromojiでIndexOutOfBoundsExceptionが出たり面倒くさい)。これで辞書のCSVファイルができます。

6/18改変:コードを少し変更しました。kuromoji-ipadicのコードでは品詞タグ4つ、活用形2つの部分に繁体字、簡体字などの素性を入れるとうまくいかないため、後半に入れました。Javaコードも少し変更してgetTraditional()などのメソッドを追加してみました。

public class Token extends TokenBase {

    public Token(int wordId,
                 String surface,
                 ViterbiNode.Type type,
                 int position,
                 Dictionary dictionary) {
        super(wordId, surface, type, position, dictionary);
    }

    public String getTraditional() {
        return this.getFeature(10);
    }

    public String getSimplified() {
        return this.getFeature(11);
    }

    public String getPinyin() {
        return this.getFeature(12);
    }
    (略)
}

char.def

IPA辞書のをもとに漢字、カタカナ、平仮名などの区分を除去してでっち上げます。ASCII文字とスペースとDEFAULTだけあればいいと思います。最後に

0xFF0C SYMBOL

を追加しています。全角カンマです。

DEFAULTの未知語処理で最大長を1文字にしています。未知語に当たった場合は1文字ずつに分割します。古代中国語は1文字=1形態素だったそうですので、そういう分割もありでしょう。現代中国語は違います。

###################################################################################
#
#  CHARACTER CATEGORY DEFINITION
#
#  CATEGORY_NAME INVOKE GROUP LENGTH
#
#   - CATEGORY_NAME: Name of category. you have to define DEFAULT class.
#   - INVOKE: 1/0:   always invoke unknown word processing, evan when the word can be found in the lexicon
#   - GROUP:  1/0:   make a new word by grouping the same chracter category
#   - LENGTH: n:     1 to n length new words are added
#
DEFAULT	       1 1 1  # DEFAULT is a mandatory category!
SPACE	       0 1 0
SYMBOL	       1 1 0
NUMERIC	       1 1 0
ASCII	       1 1 0

###################################################################################
#
# CODE(UCS2) TO CATEGORY MAPPING
#

# SPACE
0x0020 SPACE  # DO NOT REMOVE THIS LINE, 0x0020 is reserved for SPACE
0x00D0 SPACE
0x0009 SPACE
0x000B SPACE
0x000A SPACE

# ASCII
0x0021..0x002F ASCII
0x0030..0x0039 ASCII
0x003A..0x0040 ASCII
0x0041..0x005A ASCII
0x005B..0x0060 ASCII
0x0061..0x007A ASCII
0x007B..0x007E ASCII

# Latin
0x00A1..0x00BF ASCII # Latin 1
0x00C0..0x00FF ASCII  # Latin 1
0x0100..0x017F ASCII  # Latin Extended A
0x0180..0x0236 ASCII  # Latin Extended B
0x1E00..0x1EF9 ASCII  # Latin Extended Additional

# NUMERIC (�� �� �� �� �� �� ϻ �� Ȭ �� �� ɴ �� �� �� ��)
0x3007 NUMERIC # ��
0x3038 NUMERIC # ��?
0x30FB NUMERIC # ��
0x4E00 NUMERIC
0x4E8C NUMERIC
0x4E09 NUMERIC
0x56DB NUMERIC
0x4E94 NUMERIC
0x516D NUMERIC
0x4E03 NUMERIC
0x516B NUMERIC
0x4E5D NUMERIC
0x5341 NUMERIC
0x767E NUMERIC
0x5343 NUMERIC
0x4E07 NUMERIC
0x5104 NUMERIC
0x5146 NUMERIC


# OTHER SYMBOLS
0x2000..0x206F  SYMBOL # General Punctuation
0x2070..0x209F  NUMERIC # Superscripts and Subscripts
0x20A0..0x20CF  SYMBOL # Currency Symbols
0x20D0..0x20FF  SYMBOL # Combining Diaritical Marks for Symbols
0x2100..0x214F  SYMBOL # Letterlike Symbols
0x2150..0x218F  NUMERIC # Number forms
0x2100..0x214B  SYMBOL # Letterlike Symbols
0x2190..0x21FF  SYMBOL # Arrow
0x2200..0x22FF  SYMBOL # Mathematical Operators
0x2300..0x23FF  SYMBOL # Miscellaneuos Technical
0x2460..0x24FF  SYMBOL # Enclosed NUMERICs
0x2501..0x257F  SYMBOL # Box Drawing
0x2580..0x259F  SYMBOL # Block Elements
0x25A0..0x25FF  SYMBOL # Geometric Shapes
0x2600..0x26FE  SYMBOL # Miscellaneous Symbols
0x2700..0x27BF  SYMBOL # Dingbats
0x27F0..0x27FF  SYMBOL # Supplemental Arrows A
0x27C0..0x27EF  SYMBOL # Miscellaneous Mathematical Symbols-A
0x2800..0x28FF  SYMBOL # Braille Patterns
0x2900..0x297F  SYMBOL # Supplemental Arrows B
0x2B00..0x2BFF  SYMBOL # Miscellaneous Symbols and Arrows
0x2A00..0x2AFF  SYMBOL # Supplemental Mathematical Operators
0x3300..0x33FF  SYMBOL
0x3200..0x32FE  SYMBOL # ENclosed CJK Letters and Months
0x3000..0x3004  SYMBOL # CJK Symbol and Punctuation
0x3008..0x303F  SYMBOL # CJK Symbol and Punctuation
0xFE30..0xFE4F  SYMBOL # CJK Compatibility Forms
0xFE50..0xFE6B  SYMBOL # Small Form Variants
0xFF0C  SYMBOL

dicrc

IPA辞書のをそのまま使います。エンコーディングはUTF-8に

; Configuration file of IPADIC
;
; $Id: dicrc,v 1.4 2005/12/24 19:58:42 taku-ku Exp $;
;
bos-feature = BOS/EOS,*,*,*,*,*,*
cost-factor = 800
eval-size = 4
unk-eval-size = 2
config-charset = UTF8

matrix.def

MeCabだと不要ですが、Kuromojiの辞書コンパイルで必要になるので作っておきます。1×1の状態の最小構成です。

1 1
0 0 0

unk.def

未知語処理のルールです。

DEFAULT,0,0,0,*,*,*,*,*,*,*,*,*
ASCII,0,0,0,*,*,*,*,*,*,*,*,*
NUMERIC,0,0,0,*,*,*,*,*,*,*,*,*
SYMBOL,0,0,0,*,*,*,*,*,*,*,*,*
SPACE,0,0,0,*,*,*,*,*,*,*,*,*

MeCab辞書のコンパイル

これだけファイルがあれば辞書のコンパイルができるはずです。

/usr/local/Cellar/mecab/0.996/libexec/mecab/mecab-dict-index -f urf-8 -t utf-8
./pos-id.def is not found. minimum setting is used
reading ./unk.def ... 5
emitting double-array: 100% |###########################################|
./model.def is not found. skipped.
./pos-id.def is not found. minimum setting is used
reading ./cedict.csv ... 115950
emitting double-array: 100% |###########################################|
reading ./matrix.def ... 1x1

done!

mecab -d .
环球网是中国领先的国际资讯门户,拥有独立采编权的中央重点新闻网站
环球	環球,huan2 qiu2,*,*,*,*,*,*,*
网	網,wang3,*,*,*,*,*,*,*
是	是,shi4,*,*,*,*,*,*,*
中国	中國,Zhong1 guo2,*,*,*,*,*,*,*
领先	領先,ling3 xian1,*,*,*,*,*,*,*
的	的,de5,*,*,*,*,*,*,*
国际	國際,guo2 ji4,*,*,*,*,*,*,*
资讯	資訊,zi1 xun4,*,*,*,*,*,*,*
门户	門戶,men2 hu4,*,*,*,*,*,*,*
,	*,*,*,*,*,*,*,*,*
拥有	擁有,yong1 you3,*,*,*,*,*,*,*
独立	獨立,du2 li4,*,*,*,*,*,*,*
采	採,cai3,*,*,*,*,*,*,*
编	編,bian1,*,*,*,*,*,*,*
权	權,Quan2,*,*,*,*,*,*,*
的	的,de5,*,*,*,*,*,*,*
中央	中央,zhong1 yang1,*,*,*,*,*,*,*
重点	重點,chong2 dian3,*,*,*,*,*,*,*
新闻网	新聞網,xin1 wen2 wang3,*,*,*,*,*,*,*
站	站,zhan4,*,*,*,*,*,*,*
EOS

とりあえず単語分割はできます。

6/18変更

环球网是中国领先的国际资讯门户
环球	*,*,*,*,*,*,環球,环球,huan2 qiu2
网	*,*,*,*,*,*,網,网,wang3
是	*,*,*,*,*,*,是,是,shi4
中国	*,*,*,*,*,*,中國,中国,Zhong1 guo2
领先	*,*,*,*,*,*,領先,领先,ling3 xian1
的	*,*,*,*,*,*,的,的,de5
国际	*,*,*,*,*,*,國際,国际,guo2 ji4
资讯	*,*,*,*,*,*,資訊,资讯,zi1 xun4
门户	*,*,*,*,*,*,門戶,门户,men2 hu4
EOS

Kuromoji用ビルド

Javaで分割したいので、これをKuromoji用にコンパイルします。

まずbuildscript for Kuromoji with mecab-neologdを拝借します。このスクリプトをまず一度動作させます。パッケージ名オプションだけ、-p com.atilika.kuromoji.cedictと指定しておきました。パッケージ名かぶりを避けるためです。

するとkuromoji/kuromoji-ipadic以下にneologdをマージしたIPADICを生成します。neologdの場所は kuromoji/kuromoji-ipadic/dictionary/mecab-ipadic-2.7.0-20070801-neologd-20180604/ でした。この同じ階層にさきほどのCEDICTのディレクトリ一式を置きます。

つづいてダウンロードしたシェルスクリプトの最後のビルド部分を拝借して書き換えます。

mvn -pl kuromoji-ipadic -am package \
    -DskipTests=true \
    -DskipDownloadDictionary=true \
    -Dkuromoji.dict.dir=kuromoji-ipadic/dictionary/cedict \
    -Dkuromoji.dict.encoding=utf-8

辞書のパスをCEDICTにします。あとはこのコマンドだけ実行してもいいし、スクリプトに戻してスクリプトを実行してもいいでしょう。なぜこんな面倒なことをするかと言うと、このスクリプトはパッケージ名の置換をやってくれるからです。別に自力でやってもいいし、IntelliJ IDEAのようなIDEのリファクタリングを使っても構いません。

import com.atilika.kuromoji.cedict.Token;
import com.atilika.kuromoji.cedict.Tokenizer;

import java.util.List;

public class Test {

    public static void main(String[] args) {
        Tokenizer tokenizer = new Tokenizer();
        List<Token> tokens = tokenizer.tokenize("环球网是中国领先的国际资讯门户,拥有独立采编权的中央重点新闻网站");
        for( Token token : tokens )
        {
            System.out.println(token.getSurface());
        }
    }

}

結果

环
球网
是
中国
领先
的
国际
资讯
门户
,
拥有
独立
采
编
权
的
中央
重点
新闻网
站

Process finished with exit code 0

Mecabだと 环球 网 だったのにKuromojiだと环 球网 になっていますが、一応Kuromoji用にコンパイルできました。

KuromojiはMeCab互換だと言われていますが、細かいところで動作が違うので注意です。

繁体字・簡体字変換

import com.atilika.kuromoji.cedict.Token;
import com.atilika.kuromoji.cedict.Tokenizer;

import java.util.List;

public class Test {

    public static void main(String[] args) {
        Tokenizer tokenizer = new Tokenizer();
        List tokens = tokenizer.tokenize("环球网是中国领先的国际资讯门户,拥有独立采编权的中央重点新闻网站");
        for( Token token : tokens )
        {
            System.out.println(token.getTraditional());
        }
    }

}

結果

環
球網
是
中國
領先
的
國際
資訊
門戶
*
擁有
獨立
採
編
權
的
中央
重點
新聞網
站

Process finished with exit code 0

完璧ではないでしょうが、わりとしっかり繁体字と簡体字の変換ができます。