一、日文分词器 MeCab 简介

mecab (http://mecab.sourceforge.net/) 是奈良先端科学技術大学院的工藤拓开发的日文分词系统, 该作者写过多个 machine learning 方面的软件包, 最有名的就是 CRF++, 目前该作者在 google@Japan 工作。

mecab 是基于CRF 的一个日文分词系统,代码使用 c++ 实现, 基本上内嵌了 CRF++ 的代码, 同时提供了多种脚本语言调用的接口(python, perl, ruby 等).整个系统的架构采用通用泛化的设计, 用户可以通过配置文件定制CRF训练中需要使用的特征模板。 甚至, 如果你有中文的分词语料作为训练语料,可以在该架构下按照其配置文件的规范定制一个中文的分词系统。

日文NLP 界有几个有名的开源分词系统, Juman, Chasen, Mecab.   Juman 和 Chasen 都是比较老的系统了, Mecab 系统比较新, 在很多方面都优于 Juman 和 Chasen, mecab 目前开发也比较活跃。 Mecab 虽然使用 CRF 实现, 但是解析效率上确相当高效, 据作者的介绍, Mecab 比基于 HMM 的 Chasen 的解析速度要快。 笔者在一台 Linux 机器上粗略测试过其速度,将近达到 2MB/s, 完全达到了工程应用的需求, 该系统目前在日文 NLP 界被广泛使用。

中文和日文的有着类似的分词需求,因此mecab 对于中文处理来说有着很好的借鉴价值, 由于mecab 的内部模块化得很清晰,如果能读懂其文档的话,是比较容易能看懂整套代码的。 可惜目前中文的资料很少, 而其自带的文档又都是日文的, 所以了解它的中国人不多。

笔者把 mecab 自带的文档从日文翻译成中文, 希望mecab对于中文分词有兴趣的读者能有借鉴价值。日语水平很烂, 大家凑合着看吧。 对于自由的文档翻译,有一句话: Document is like sex. If it's good, it's very very good. If it's bad, it's better than nothing.

二、关于 MeCab (和布蕪)

Mecab 是京都大学情报学研究科-日本电信电话股份有限公司通信科学基础研究所通过 Unit Project 的合作研究共同开发的词法分析引擎。其设计的基本方针是不依赖于具体的语言,词典,语料库, 采用 Conditional Random Fields (CRF) 模型进行参数估计, 性能优于使用隐马模型的 ChaSen 。同时, 平均解析速度高于 ChaSenJumanKAKASI 这些日文词法分析器. 顺便说一下, Mecab (和布蕪, めかぶ), 是作者最喜欢的食物.

目录

  • 特征

    • 不依赖于词典,语料的通用设计
    • 基于条件随机场(CRF)模型,解析精度高
    • 比 ChaSen 和 KAKASI 速度快
    • 词典检索的算法/数据结构使用双数组 Double-Array.
    • 库函数可重入再入
    • 各种脚本语言接口绑定(perl/ruby/python/java/C#)

    比较

    MeCab ChaSen JUMAN KAKASI
    解析模型 bi-gram 马尔科夫模型 可变长 马尔科夫模型 bi-gram 马尔科夫模型 最长一致
    cost 估计 从语料库学习 从语料库学习 人手 没有 cost 的概念
    学习模型 CRF (区别式模型) HMM (生成式模型)
    词典检索算法 Double Array Double Array Patricia Tree Hash?
    求解算法 Viterbi Viterbi Viterbi 决定的?
    连接表的实现 2元 Table 自动机 2元 Table? 没有连接表?
    词性层级 无限制多级词性 无限制多级词性 固定2级 没有词性概念?
    未登陆词处理 字符种类 (动作定义可变更) 字符种类 (不可变更) 字符种类 (不可变更)
    带约束的解析 可能 2.4.0 以后可能 不可能 不可能
    N-best解 可能 不可能 不可能 不可能

    Mecab 为止词法分析器的开发历史请参考 这里

    邮件列表

    最新消息

    • 2008-02-03 MeCab 0.97
      • 修正多线程环境中词典打开时,独占控制不正常的 bug
      • Windows版本安装的时候, 可以指定词典的编码
      • 修正某些编译器无法编译的问题
      • 修改部分解析模式, 添加相应的 API(Tagger::set_partial())
      • 添加用于修改 word-lattice 生成级别的API (Tagger::set_lattice_level())
      • 添加修改温度参数的 API (Tagger::set_theta())
      • 添加变换到输出全切分模式的 API (Tagger::set_all_morphs())
    • 2007-06-10 MeCab 0.96
      • 修正缓冲区溢出的 bug
      • 设定为总是生成 POS-ID(-p 选项废除)
      • 词典分隔符从 : 改为 ,(CSV)
      • 修正了 charset 判定的 bug, 以及由此导致的 用户词典和系统词典 不兼容的 bug
      • 修正用户词典和系统词典的字符编码不一致的时候, 无法生成词典的 bug
      • 添加 --dump-config 选项, 把命令行选项 dump 出来
      • 添加基于 EM 的 HMM训练的函数 (experimental)
    • 2007-03-11 MeCab 0.95
      • 修正老的编译器无法编译的问题
      • csvのエスケープの不具合で ","を含む単語が追加できなかった問題を修正
      • 修正一些 UTF8 词典无法正常生成的 bug
      • recall/precisionの表示が反対になっていたバグの修正
      • 修正命令行解析错误
      • 修正其他小 bug
    • 2007-02-24MeCab 0.94
      • 修正很多 bug
      • 加入 HMM 训练的支持 (experimental)
      • 添加 API 获取解析结果的所有信息 (begin_node_list, end_node_list)
      • char.def, unk.def, matrix.def 没有定义的时候, 修改为也可以顺利生成词典
      • 废除Windows版对 iconv.dll的依赖
      • 清理代码
    • 2006-07-30 MeCab 0.93
      • License 从 LGPL 修改为 BSD,LGPL,GPL
    • 2006-07-10 MeCab 0.92
      • 词典编译等, 一部分由 perl 实现的脚本使用 C++ 重写, 排除对 Perl 的依赖。
      • 词典编译 (mecab-dict-index) 高速化
      • 修改 rewrite.def 的语法
      • 追加 -x 选项用于指定未登录词词性
      • 支持词性 id
      • 修正字符种类信息在某些训练中失败的 bug
      • 其他小 bug 的修正
    • 2006-04-30 MeCab 0.91
      • 修正Windows 环境中字符串末尾半角空格丢失的 bug
      • 修正连接表前件和后见的大小不同时无法正常解析的bug
      • 在 mecab-dict-index 中添加 -f 选项, 使得用户可以指定 CSV 词典文件的编码
      • 修正一些 API 函数无法 export 的问题
      • CRF 训练时使用 pthread 加入并行支持 (experimental)
      • 修正无法生成用户词典的问题
      • example 目录中添加 MeCab 使用的例子 (unittest)
      • 其他小 bug 的修正
    • 2006-03-26 MeCab 0.90
      • Initial release!

    下载

    • MeCab 是自由软件.遵从GPL(the GNU General Public License), LGPL(Lesser GNU General Public License), 以及 BSD 许可证, 可以再发布, 详细说明请参看随附的 COPYING, GPL, LGPL, BSD 这几个文件.
    • MeCab 本体

      Source

      • mecab-0.97.tar.gz:下载
      • 不包含词典, 运行时词典是必需.

    Binary package for MS-Windows

    • mecab-0.97.exe:下载
    • Windows 版中包含编译好的 IPA 词典
    • MeCab 使用的词典

      IPA 词典

      • IPA 辞書, 基于IPA语料库, 使用 CRF 进行参数估计的词典 (推荐) 下载

      Juman 词典

      • Juamn 词典, 基于京都语料库, 使用 CRF 进行参数估计的词典 下载

      Canna dic

      • Canna 词典: 公开预定
    • perl/ruby/python/java 绑定

    安装

    UNIX

    • 运行依赖
      • C++ 编译器 (g++ 3.4.3 和 VC7 确认可以编译通过)
      • iconv (libiconv): 用于词典的编码转换
    • 安装步骤一般的自由软件的安装步骤即可.
       % tar zxfv mecab-X.X.tar.gz
       % cd mecab-X.X
       % ./configure 
       % make
       % make check
       % su
       # make install

      词典的安装

      % tar zxfv mecab-ipadic-2.7.0-XXXX.tar.gz
      % mecab-ipadic-2.7.0-XXXX
      % ./configure
      % make
      % su
      # make install

    Windows

    从二进制安装的场合, 运行自解压安装程序 (mecab-X.X.exe). 词典也会同时装上.

    使用方法

    首先尝试词法解析

    mecab 启动的时候, 从标准输入读取数据,逐行对文本进行解析 .

    % mecab
    すもももももももものうち
    すもも  名詞,一般,*,*,*,*,すもも,スモモ,スモモ
    も      助詞,係助詞,*,*,*,*,も,モ,モ
    もも    名詞,一般,*,*,*,*,もも,モモ,モモ
    も      助詞,係助詞,*,*,*,*,も,モ,モ
    もも    名詞,一般,*,*,*,*,もも,モモ,モモ
    の      助詞,連体化,*,*,*,*,の,ノ,ノ
    うち    名詞,非自立,副詞可能,*,*,*,うち,ウチ,ウチ
    EOS

    输出格式和 ChaSen 有较大差异, 从左开始,依次为,

    表层形\t词性,词性细分类1,词性细分类2,词性细分类3,活用形,活用型,原形,读音,发音

    如果给定输入文件参数, 则该文件成为解析对象. 另外, -o 选项可以用来指定输出文件 .

    % mecab INPUT -o OUTPUT

    逐词分隔输出

    使用 -O 选项可以得到如下输出格式.

    % mecab -O wakati
    太郎はこの本を二郎を見た女性に渡した。
    太郎 は この 本 を 二郎 を 見 た 女性 に 渡し た 。

    输出格式变更

    使用 -O 选项可以指定如下输出格式.

    % mecab -Oyomi (包含读音)
    % mecab -Ochasen (ChaSen兼容格式)
    % mecab -Odump (输出所有信息)

    这些输出格式都在 /usr/local/lib/mecab/ipadic/dicrc 文件中定义,另外, 用户也可以自由定义输出格式, 请参看 这里.

    高级使用方法

    文字编码转换

    没有特别说明, 缺省使用 euc 编码. 如果要使用 shift-jis 和 utf8 编码, 可以修改词典的 configure 脚本中 charset 选项, 重新编译词典, 这样就能生成 shift-jis 和 utf8 编码的词典.

    % tar zxfv mecab-ipadic-2.7.0-xxxx
    % cd mecab-ipadic-2.7.0-xxxx
    % ./configure --with-charset=sjis
    % make
    
    % tar zxfv mecab-ipadic-2.7.0-xxxx
    % ./configure --with-charset=utf8
    % make

    另外, 使用 mecab-dict-index 的 -t 选项可以重新生成不同文字编码的词典。 -f 选项为原始文本格式词典的文字编码.

    % cd mecab-ipadic-2.7.0-xxxx
    % /usr/local/libexec/mecab/mecab-dict-index -f euc-jp -t utf-8
    # make install

    UTF-8 only mode

    如果指定了 configure option 中 --enable-utf8-only 选项, 则 MeCab 固定使用 utf8 编码。在支持 euc-jp, shift-jis 编码的时候, MeCab 程序内部需要生成编码转换表; 指定--enable-utf8-only 选项后, 不生成该编码转换表, 由此可以减小最后生成的二进制程序的大小。

    未登陆词猜测

    MeCab 遇到未登陆词的时候会对其词性进行适当的猜测 .

    ホリエモン市
    ホリエモン      名詞,固有名詞,地域,一般,*,*,*
    市      名詞,接尾,地域,*,*,*,市,シ,シ
    EOS
    ホリエモンさん
    ホリエモン      名詞,固有名詞,人名,一般,*,*,*
    さん    名詞,接尾,人名,*,*,*,さん,サン,サン

    但是不能保证猜测的正确率。 要关闭词性猜测的功能,把未登陆词词性标定为 "未登陆词", 可以使用 -x (--unk-feature) 选项, 该选项指定的字符串将被标定为未登陆词的词性.

    %mecab --unk-feature "未知語" 
    ホリエモンさん
    ホリエモン      未知語
    さん    名詞,接尾,人名,*,*,*,さん,サン,サン

    N-Best 解输出

    使用 -N #NUM 选项, 将输出最可能的前 #NUM 个结果, 理论上可以输出所有可能的解析结果, 由于输出缓存大小的限制, -N 的最大值限制为 512 .

    % mecab -N2
    今日もしないとね。
    今日    名詞,副詞可能,*,*,*,*,今日,キョウ,キョー
    も      助詞,係助詞,*,*,*,*,も,モ,モ
    し      動詞,自立,*,*,サ変?スル,未然形,する,シ,シ
    ない    助動詞,*,*,*,特殊?ナイ,基本形,ない,ナイ,ナイ
    と      助詞,接続助詞,*,*,*,*,と,ト,ト
    ね      助詞,終助詞,*,*,*,*,ね,ネ,ネ
    。      記号,句点,*,*,*,*,。,。,。
    EOS
    今日    名詞,副詞可能,*,*,*,*,今日,キョウ,キョー
    もし    副詞,一般,*,*,*,*,もし,モシ,モシ
    ない    形容詞,自立,*,*,形容詞?アウオ段,基本形,ない,ナイ,ナイ
    と      助詞,接続助詞,*,*,*,*,と,ト,ト
    ね      助詞,終助詞,*,*,*,*,ね,ネ,ネ
    。      記号,句点,*,*,*,*,。,。,。
    EOS

    致谢

    CRF 的参数估计使用了 Jorge Nocedal 发现的 L-BFGS 方法以及他的 FORTRAN 实现, 在此表示感谢。

    http://www.ece.northwestern.edu/~nocedal/lbfgs.html

    • J. Nocedal. Updating Quasi-Newton Matrices with Limited Storage (1980), Mathematics of Computation 35, pp. 773-782.
    • D.C. Liu and J. Nocedal. On the Limited Memory Method for Large Scale Optimization (1989), Mathematical Programming B, 45, 3, pp. 503-528.

    libmecab.html

    三、MeCab 库函数接口

    C 库函数说明

    以下提供了 C 的函数接口.

    mecab_t *mecab_new (int argc, char **argv)
    生成 mecab 的实例.
    参数为 main 函数风格的参数, argc, argv. 这些参数的处理方式和 mecab 命令行 的处理方式是相同给定。
    调用成功时, 返回 mecab_t 类型的指针, 使用该指针进行词法解析。 调用失败返回 NULL. 
    mecab_t *mecab_new2 (const char *arg)
    生成 mecab 的实例.
    函数参数为字符串格式。 调用成功时, 返回 mecab_t 类型的指针, 使用该指针进行词法解析。 
    const char *mecab_version()
    取得 mecab 的 version 字符串.
    const char *mecab_strerror (mecab_t* m)
    取得错误的内容(字符串格式). mecab_sparse_tostr 等函数返回 NULL 的时候,
    可以调用 mecab_strerror 函数获取 错误的内用。 获取 mecab_new,mecab_new2 时, m 指定为 NULL . 
    const char *mecab_sparse_tostr (mecab_t *m, const char *str)
    实行词法解析。参数m为 mecab_new 返回的 mecab_t 类型的指针,
    待解析的字符串通过 char 型指针指定. 解析结果通过 char 型指针 返回, 解析失败返回 NULL.
    返回指针所指向的内存无需调用者管理, 每次调用 mecab_sparse_tostr 的时候会重写这块内存,
    调用 mecab_destroy 的时候这块内存会被释放。
    const char *mecab_sparse_tostr2 (mecab_t *m, const char *str, size_t len)
    和 mecab_sparse_tostr 函数相当, len 指定需要解析的句子的长度。 
    char *mecab_sparse_tostr3 (mecab_t *m, const char *istr,size_t ilen char *ostr, size_t olen)
    类似于 mecab_sparse_tostr2 , 但可以指定输出 buffer(ostr) 及其长度 (olen).
    ostr 内存由调用方管理。 函数调用成功的时候,返回 char 型的指针,
    该返回指针 等同于 ostr. 如果解析结果的长度超过 olen, 解析失败, 返回 NULL. 
    const char *mecab_nbest_sparse_tostr (mecab_t *m, size_t N, const char *str)
    mecab_sparse_tostr () 的 N-Best 版本。N 为解析结果的个数.
    使用 N-Best 机能的时候, mecab_new 必须指定 -l 1 选项。
    const char *mecab_nbest_sparse_tostr2 (mecab_t *m, size_t N, const char *str, size_t len)
    mecab_sparse_tostr2 () 的输出 N-Best 解的版本. N 为解析结果的个数. 
    char *mecab_nbest_sparse_tostr3 (mecab_t *m, size_t N, const char *str, size_t len, char *ostr, size_t olen)
    mecab_sparse_tostr3 () 的输出 N-Best 解的版本. N 为解析结果的个数. 
    int mecab_nbest_init (mecab_t* m, const char* str);
    按顺序获取近似正确的N-Best 解析结果的时候, 先要调用该函数进行初始化. str 指定待解析的句子.
    初始化成功返回1, 失败返回0, 失败的原因通过 mecab_strerror 获取。 
    int mecab_nbest_init2 (mecab_t* m, const char* str, len);
    类似于 mecab_nbest_init () , len 指定句子的长度。
    const char *mecab_nbest_next_tostr (mecab_t* m)
    mecab_nbest_init() 调用之后,调用该函数按顺序获取近似正确的 N-Best 结果。
    调用失败时(例如, 所有解都获取完了)返回 NULL, 失败原因由 mecab_strerror 获取。 
    char *mecab_nbest_next_tostr2 (mecab_t *m , char *ostr, size_t olen)
    类似于 mecab_nbest_next_tostr(), ostr, olen 用于指定输出 buffer。
    调用失败时返回 NULL, 失败原因由 mecab_strerror 获取。
    void mecab_destroy(mecab_t *m)
    释放 mecab_t 型指针.

    要获取词条信息时, 使用 mecab_node_t 结果体 和 mecab_sparse_tonode 函数

    #define MECAB_NOR_NODE  0
    #define MECAB_UNK_NODE  1
    #define MECAB_BOS_NODE  2
    #define MECAB_EOS_NODE  3
    
    struct mecab_node_t {
      struct mecab_node_t  *prev;  // 前一个词条的指针
      struct mecab_node_t  *next;  // 后一个词条的指针
    
      struct mecab_node_t  *enext; // 同一位置结束的词条的指针 
      struct mecab_node_t  *bnext; // 同一位置开始的词条的指针 
    
      char  *surface;             // 词条的表面字符串 
                                  // 没有以 NUL 结束. 因此要获取字符串, 需要使用 
    			      // strncpy(buf, node->feature, node->length) 
    
      char  *feature;             // CSV 格式的特征 
      unsigned int   length;      // 词条的长度
      unsigned int   rlength;     // 词条的长度(包括开始处的空格)
      unsigned int   id;          // 词条的唯一标识 ID 
      unsigned short rcAttr;      // 下文语境 idid
      unsigned short lcAttr;      // 上文语境  id
      unsigned short posid;       // 词条词性特征 ID
      unsigned char  char_type;   // 字符类别信息
      unsigned char  stat;        // 词条类别: 可用以下几个宏
                                  // #define MECAB_NOR_NODE  0
                                  // #define MECAB_UNK_NODE  1
                                  // #define MECAB_BOS_NODE  2
                                  // #define MECAB_EOS_NODE  3
      unsigned char  isbest;      // 最优解场合为 1, 其他为 0
    
      float          alpha;       // forward backward 的 forward log 概率
      float          beta;        // forward backward 的 backward log 概率
      float          prob;        // 周边概率
                                  // alpha, beta, prob 在 -l 2 选项指定的时候定义 
    
      short          wcost;       // 词条生成 cost 
      long           cost;        // 累计 cost 
    };
    const mecab_node_t *mecab_sparse_tonode (mecab_t *m, const char *str)
    执行解析操作, 参数 m 为 mecab_new 中得到的 mecab_t 类型的指针。
    待解析的句子为 char 型的字符串。函数调用成功时,返回句首词条(mecab_node_t 类型)对应的指针;失败时返回 NULL.
    mecab_node_t 为双向链表,可以使用 next, prev 按序访问所有的此条。
    函数返回指针所对应的内存无须调用方管理, mecab_sparse_tonode 函数被调用时会覆盖重写这块内存,
    mecab_destroy 函数调用时释放对应内存。 
    const mecab_node_t *mecab_sparse_tonode2 (mecab_t *m, const char *str, size_t len)
    类似于 mecab_sparse_tonode , len 指定待解析的句子长度. 
    const mecab_node_t *mecab_next_tonode (mecab_t* m)
    类似于 mecab_next_tostr [1] , 只是不返回字符串, 返回 mecab_node_t 类型的词条信息。

    要读取词典的信息时, 使用 mecab_dictionary_t 结构体和 mecab_dictionary_info() 函数。

    #define MECAB_USR_DIC   1
    #define MECAB_SYS_DIC   0
    #define MECAB_UNK_DIC   2
    
    struct mecab_dictionary_info_t {
      const char                     *filename;  // 词典文件名 
      const char                     *charset;   // 词典编码
      unsigned int                    size;      // 词条书目
      int                             type;      // 词典类型,  可选值为 MECAB_(USR|SYS|UNK)_DIC  
      unsigned int                    lsize;     // 上文语境 ID 大小
      unsigned int                    rsize;     // 下文语境 ID 大小
      unsigned short                  version;   // 版本
      struct mecab_dictionary_info_t *next;   // 指向下一个词典的指针
    };
    const mecab_dictionary_info_t *mecab_dictionary_info(mecab_t *m)
    获取词典信息, 参数 m 为 mecab_new 得到的 mecab_t 类型的指针。
    mecab_dictoinary_info_t 为单向链表, 包含多个词典的时候, 可以使用 next 访问到所有的词典信息。
    mecab_dictionary_info() 函数返回的内存指针由 mecab 自己管理,调用者无须释放。

    以下API 用于修改解析时的参数设置。

    int mecab_get_partial(mecab_t *m)
    取得当前的部分解析模式。(0: off, 1:on)
    void mecab_set_partial(mecab_t *m)
    设定当前的部分解析模式。(0: off, 1:on)
    float mecab_get_theta(mecab_t *m)
    软性逐词分隔输出的温度参数的获取。
    void mecab_set_theta(mecab_t *m, float theta)
    软性逐词分隔输出的温度参数的设定。
    int mecab_get_lattice_level(mecab_t *m)
    取得 lattice-level(解析时生成哪个层级的 lattice 信息) 的值

    • 0: 可以输出最优解 (缺省, 高速)
    • 1: 可以输出 N-best 解 (中速)
    • 2: 软性逐词分隔输出(译者注: 类似于全切分) (低速)
    void mecab_set_lattice_level(mecab_t *m, int lattice_level)
    设定 lattice 层级。
    int mecab_get_all_morphs(mecab_t *m)
    取得输出模式。(0: 最优解, 1:全切分)
    void mecab_set_all_morphs(mecab_t *m, int all_mophrs)
    设定输出模式。(0: 最优解, 1:全切分)

    C 调用样例

    example/example.c

    #include <mecab.h>
    #include <stdio.h>
    
    #define CHECK(eval) if (! eval) { \
        fprintf (stderr, "Exception:%s\n", mecab_strerror (mecab)); \
        mecab_destroy(mecab); \
        return -1; }
    
    int main (int argc, char **argv) {
      char input[1024] = "太郎は次郎が持っている本を花子に渡した。";
      mecab_t *mecab;
      mecab_node_t *node;
      const char *result;
      int i;
    
      mecab = mecab_new (argc, argv);
      CHECK(mecab);
    
      result = mecab_sparse_tostr(mecab, input);
      CHECK(result)
      printf ("INPUT: %s\n", input);
      printf ("RESULT:\n%s", result);
    
      result = mecab_nbest_sparse_tostr (mecab, 3, input);
      CHECK(result);
      fprintf (stdout, "NBEST:\n%s", result);
    
      CHECK(mecab_nbest_init(mecab, input));
      for (i = 0; i < 3; ++i) {
        printf ("%d:\n%s", i, mecab_nbest_next_tostr (mecab));
      }
    
      node = mecab_sparse_tonode(mecab, input);
      CHECK(node);
      for (; node; node = node->next) {
        fwrite (node->surface, sizeof(char), node->length, stdout);
        printf("\t%s\n", node->feature);
      }
    
      node = mecab_sparse_tonode(mecab, input);
      CHECK(node);
      for (;  node; node = node->next) {
        printf("%d ", node->id);
    
        if (node->stat == MECAB_BOS_NODE)
          printf("BOS");
        else if (node->stat == MECAB_EOS_NODE)
          printf("EOS");
        else
          fwrite (node->surface, sizeof(char), node->length, stdout);
    
        printf(" %s %d %d %d %d %d %d %d %d %f %f %f %d\n",
    	   node->feature,
    	   (int)(node->surface - input),
    	   (int)(node->surface - input + node->length),
    	   node->rcAttr,
    	   node->lcAttr,
    	   node->posid,
    	   (int)node->char_type,
    	   (int)node->stat,
    	   (int)node->isbest,
    	   node->alpha,
    	   node->beta,
    	   node->prob,
    	   node->cost);
      }
    
      mecab_destroy(mecab);
    
      return 0;
    }

    C++ 库函数说明

    以下为 C++ API 接口, 基本上同 C 的接口相同。

    • mecab_t 对应于 MeCab::Tagger
    • mecab_node_t 对应于 MeCab::Node
    • mecab_new() 对应于 MeCab::createTagger() 工厂函数
    • mecab_destroy () 使用 delete 释放内存
    namespace MeCab {
      typedef struct mecab_node_t                Node;
      typedef struct mecab_dictionary_info_t     DictionaryInfo;
    
      class Tagger {
      public:
        virtual const char* parse(const char*, size_t, char*, size_t) = 0;
    
        virtual const char* parse(const char*, size_t = 0) = 0;
        virtual Node* parseToNode(const char*, size_t = 0) = 0;
    
        virtual const char* parseNBest(size_t, const char*, size_t = 0) = 0;
        virtual bool  parseNBestInit(const char*, size_t = 0) = 0;
        virtual Node*  nextNode() = 0;
        virtual const char* next() = 0;
        virtual const char* formatNode(Node *) = 0;
    
        virtual const char* next(char*, size_t) = 0;
        virtual const char* parseNBest(size_t, const char*,
                                       size_t, char *, size_t) = 0;
        virtual const char* formatNode(Node *, char *, size_t) = 0;
    
        virtual bool  partial() const                             = 0;
        virtual void  set_partial(bool partial)                   = 0;
        virtual float theta() const                               = 0;
        virtual void  set_theta(float theta)                      = 0;
        virtual int   lattice_level() const                       = 0;
        virtual void  set_lattice_level(int level)                = 0;
        virtual bool  all_morphs() const                          = 0;
        virtual void  set_all_morphs(bool all_morphs)             = 0;
    
        virtual const char* what() = 0;
    
        virtual const DictionaryInfo* dictionary_info() const = 0;
    
        virtual ~Tagger() {};
    
        static const char *version();
    
        static Tagger* create(int, char**);
        static Tagger* create(const char*);
      };
    
      /* factory method */
      Tagger *createTagger (int, char**);
      Tagger *createTagger (const char*);
      const char* getTaggerError ();
    }

    C++ 样例

    #include <iostream>
    #include <mecab.h>
    
    #define CHECK(eval) if (! eval) { \
       const char *e = tagger ? tagger->what() : MeCab::getTaggerError(); \
       std::cerr << "Exception:" << e << std::endl; \
       delete tagger; \
       return -1; }
    
    int main (int argc, char **argv) {
      char input[1024] = "太郎は次郎が持っている本を花子に渡した。";
    
      MeCab::Tagger *tagger = MeCab::createTagger (argc, argv);
      CHECK(tagger);
    
      const char *result = tagger->parse(input);
      CHECK(result);
      std::cout << "INPUT: " << input << std::endl;
      std::cout << "RESULT: " << result << std::endl;
    
      result = tagger->parseNBest(3, input);
      CHECK(result);
      std::cout << "NBEST: " << std::endl << result;
    
      CHECK(tagger->parseNBestInit(input));
      for (int i = 0; i < 3; ++i) {
        std::cout << i << ":" << std::endl << tagger->next();
      }
    
      MeCab::Node* node = tagger->parseToNode(input);
      CHECK(node);
      for (; node; node = node->next) {
        std::cout.write(node->surface, node->length);
      }
    
      node = tagger->parseToNode(input);
      CHECK(node);
    
      for (; node; node = node->next) {
        std::cout << node->id << ' ';
        if (node->stat == MECAB_BOS_NODE)
          std::cout << "BOS";
        else if (node->stat == MECAB_EOS_NODE)
          std::cout << "EOS";
        else
          std::cout.write (node->surface, node->length);
    
        std::cout << ' ' << node->feature
    	      << ' ' << (int)(node->surface - input)
    	      << ' ' << (int)(node->surface - input + node->length)
    	      << ' ' << node->rcAttr
    	      << ' ' << node->lcAttr
    	      << ' ' << node->posid
    	      << ' ' << (int)node->char_type
    	      << ' ' << (int)node->stat
    	      << ' ' << (int)node->isbest
    	      << ' ' << node->alpha
    	      << ' ' << node->beta
    	      << ' ' << node->prob
    	      << ' ' << node->cost << std::endl;
      }
    
      delete tagger;
    
      return 0;
    }

    编译方法

      • UNIX 场合
    % cc -O2 `mecab-config --cflags` example.c -o example \
             `mecab-config --libs`
      • Windows 场合

    首先, 把 include\mecab.h, bin\libmecab.dll lib\libmecab.lib 复制到执行编译的目录中, 后面的操作依不同的编译器有所不同。

        • cygwin/mingw 环境
    % gcc -DDLL_IMPORT -I. example.c -o example.exe libmecab.dll
        • VC++ 环境
    % cl -DDLL_IMPORT -I. example.c libmecab.lib

    有关多线程

    MeCab 支持多线程, 一个线程使用一个 (mecab_t *) 实例的时候是线程安全的。 另外,多个线程共享同一个词典, 词典是可以再利用的资源, 生成多个实例的时候无需使用过多内存。

    一个实例在多个线程上使用的时候需要适当的加锁控制, 由于会导致较差的性能, 所以不推荐使用。

    译者注

    [1] 原文为 mecab_next_tostr, 但是本文件的 API 中不存在该函数, 估计为笔误

     dic.html

    三、词条追加方法

    概要

    有两种方法可以往词典中追加词条.

    • 在系统词典中追加
    • 在用户词典中追加

    在系统词典中追加

    词典更新不频繁, 同时不想降低解析速度时,最好直接修改系统词典.

    • 进入包含文件 mecab-ipadic 的目录
    • 创建文件 foo.csv (扩展名不用 .csv 也可以)
    • foo.csv 文件中添加词条
    • 重新编译安装词典
      % /usr/local/libexec/mecab/mecab-dict-index -f euc-jp -t euc-jp
      % su
      # make install
      • -f charset: CSV 文件的编码
      • -t charset: 生成的二进制词典的编码

    例: 生成 utf-8 编码词典的例子

    % /usr/local/libexec/mecab/mecab-dict-index -f euc-jp -t utf8

    在用户词典中追加

    更新系统词典比较耗时; 词典更新频繁, 或者没有权限变更系统词典的时候, 也可以使用用户词典。

    • 进入适当的目录 (例: /home/foo/bar)
    • 创建 foo.csv 这种文件
    • 在 foo.csv 中添加词条
    • 编译词典
      % /usr/local/libexec/mecab/mecab-dict-index -d/usr/local/lib/mecab/dic/ipadic \
      -u foo.dic -f euc-jp -t euc-jp foo.csv
      • -d DIR: 包含系统词典的目录
      • -u FILE: FILE 生成的用户词典文件
      • -f charset: CSV 文件的编码
      • -t charset: 生成的二进制词典的编码
    • 确认生成了词典文件 /home/foo/bar/foo.dic
    • 在 /usr/local/lib/mecab/dic/ipadic/dicrc 或者 /usr/local/etc/mecabrc 中追加如下行
      userdic = /home/foo/bar/foo.dic
    • 没有权限修改文件 /usr/local/etc/mecabrc 的时候,把 /usr/local/etc/mecabrc 复制为 ~/.mecabrc 后, 添加如上的行
    • userdic 可以按照 CSV 格式指定多个
       userdic = /home/foo/bar/foo.dic,/home/foo/bar2/usr.dic,/home/foo/bar3/bar.dic

    词条的格式 (没有活用的词条)

    系统词典, 用户词典中词条的格式是相同的.

    每个词条, 按如下的 CSV 格式添加,如名词等没有活用的词条, 记录很简单 .

    工藤,1223,1223,6058,名詞,固有名詞,人名,名,*,*,くどう,クドウ,クドウ

    从左开始依次为,

    表层形, 上文语境ID, 下文语境ID, cost, 词性,词性细分类1,词性细分类2,词性细分类3,活用形,活用型,原形,读音,发音

    cost 值用于表示词条出现的容易程度,cost 值小表示词条容易出现. コストは,その単語がどれだけ出現しやすいかを示しています. 小さいほど, 出現しやすいという意味になります. 似たような単語と 同じスコアを割り振り, その単位で切り出せない場合は, 徐々に小さくしていけばいいと思います.

    上文语境 ID 是从该词条左边开始的语境的内部状态 ID, 从文件 left-id.def 中选择这些ID, 该文件通常在系统词典所在的目录中. 如果文件中只有 ID -1, mecab-dict-index 将自动的对 ID 赋值.

    下文语境 ID 是从该词条右边开始的语境的内部状态 ID, 从文件 right-id.def 中选择这些ID, 该文件通常在系统词典所在的目录中. 如果文件中只有 ID -1, mecab-dict-index 将自动的对 ID 赋值.

    另外,用户可以按 CSV 格式添加自己喜欢的信息 .

    ユーザ設定,-1,-1,10,名詞,一般,*,*,*,*,ユーザ設定,ユーザセッテイ,ユーザセッテイ,追加エントリ
    运行例子:
    % mecab 
    ユーザ設定が必要です。
    ユーザ設定      名詞,一般,*,*,*,*,ユーザ設定,ユーザセッテイ,ユーザセッテイ,追加エントリ
    が      助詞,格助詞,一般,*,*,*,が,ガ,ガ
    必要    名詞,形容動詞語幹,*,*,*,*,必要,ヒツヨウ,ヒツヨー
    です    助動詞,*,*,*,特殊・デス,基本形,です,デス,デス
    。      記号,句点,*,*,*,*,。,。,。
    EOS

    词条的格式 (活用的词条)

    可以活用的词条, 用户必须自己把所有的活用情形展开, 比较麻烦。以下是 "いそがしい" 这个词条所有的活用词展开的情形 .

    いそがしい,120,120,6078,形容詞,自立,*,*,形容詞・イ段,基本形,いそがしい,イソガシイ,イソガシイ
    いそがし,128,128,6080,形容詞,自立,*,*,形容詞・イ段,文語基本形,いそがしい,イソガシ,イソガシ
    いそがしから,136,136,6079,形容詞,自立,*,*,形容詞・イ段,未然ヌ接続,いそがしい,イソガシカラ,イソガシカラ
    いそがしかろ,132,132,6079,形容詞,自立,*,*,形容詞・イ段,未然ウ接続,いそがしい,イソガシカロ,イソガシカロ
    いそがしかっ,148,148,6078,形容詞,自立,*,*,形容詞・イ段,連用タ接続,いそがしい,イソガシカッ,イソガシカッ
    いそがしく,152,152,6078,形容詞,自立,*,*,形容詞・イ段,連用テ接続,いそがしい,イソガシク,イソガシク
    いそがしくっ,152,152,6079,形容詞,自立,*,*,形容詞・イ段,連用テ接続,いそがしい,イソガシクッ,イソガシクッ
    いそがしゅう,144,144,6079,形容詞,自立,*,*,形容詞・イ段,連用ゴザイ接続,いそがしい,イソガシュウ,イソガシュウ
    いそがしゅぅ,144,144,6079,形容詞,自立,*,*,形容詞・イ段,連用ゴザイ接続,いそがしい,イソガシュゥ,イソガシュゥ
    いそがしき,124,124,6079,形容詞,自立,*,*,形容詞・イ段,体言接続,いそがしい,イソガシキ,イソガシキ
    いそがしけれ,108,108,6079,形容詞,自立,*,*,形容詞・イ段,仮定形,いそがしい,イソガシケレ,イソガシケレ
    いそがしかれ,140,140,6079,形容詞,自立,*,*,形容詞・イ段,命令e,いそがしい,イソガシカレ,イソガシカレ
    いそがしけりゃ,112,112,6079,形容詞,自立,*,*,形容詞・イ段,仮定縮約1,いそがしい,イソガシケリャ,イソガシケリャ
    いそがしきゃ,116,116,6079,形容詞,自立,*,*,形容詞・イ段,仮定縮約2,いそがしい,イソガシキャ,イソガシキャ
    いそがし,104,104,6080,形容詞,自立,*,*,形容詞・イ段,ガル接続,いそがしい,イソガシ,イソガシ

    chasen 在 grammar.cha cforms.char 文件中描述词法, 解析的时候展开. 这是对词条的活用动态展开, 因此, chasen 中只要添加词条的原形(基本型)即可。

    mecab 无法在解析中展开, 生成词典的时候使用静态展开的策略(静态活用展开). 这样做的理由和计算机的速度及资源相关联。 chasen 开发的时候, 内存大小受限,难以使用静态展开的方法。 现在, 由于内存和硬盘的空间都很充裕, 所以使用静态展开的策略, 可以提升解析的速度。

    将来会考虑加入如下框架: 自动从词法描述中把活用静态展开; 目前,活用的展开完全交给用户自己完成。

    format.html

    四、输出格式

    概要

    MeCab 和 ChaSen 一样, 可以比较自由的定义输出格式. Mecab 在配置文件中可以定义多种输出格式, 运行的时候可以切换不同的输出格式, 这是 MeCab 独有的功能。

    输出格式的指定

    可以修改以下4种输出格式.

    • node: 输出一个词语,缺省为空
    • unk: 输出一个未登陆词, 缺省使用 node 输出格式
    • bos: 句首的输出 , 缺省为空
    • eos: 句尾的输出, 缺省为 "EOS\n"

    没有显示指定输出格式的时候, 将使用缺省的格式 .

    可以使用两种方式指定输出格式.

    • 通过命令行指定
      % mecab --node-format=STR --bos-format=STR --eos-format=STR --unk-format=STR
    • 通过 mecabrc 配置文件指定使用任意的字符串 KEY, 用户可以在 mecabrc 中定义相应的输出格式.
      node-format-KEY = STR
      unk-format-KEY = STR
      eos-format-KEY = STR
      bos-format-KEY = STR

      在命令行中可以指定使用 KEY 对应的输出格式.

      % mecab -Okey

    输出格式

    %s 词条种类(0: 普通, 1: 未登陆词, 2:句首, 3:句尾)
    %S 输入的句子
    %L 输入句子的长度
    %m 词条的表层字符串
    %M 词条的表层字符串, 但其中包含的空白文字也会输出 (参照 %pS )
    %h 素性的内部 ID
    %% 百分号 %
    %c 单语的 cost
    %H 素性 (词性, 活用, 读音み) 字符串,CSV 格式
    %t 字符类型 id
    %P 周边概率 (仅在 -l2 选项指定的时候有效)
    %pi 形態素に付与されるユニークなID
    %pS もし形態素が空白文字列で始まる場合は, その空白文字列を表示 %pS%m と %M は同一
    %ps 开始位置
    %pe 结束位置
    %pC 同前一个词条的连接 cost
    %pw 等同于 %c
    %pc 连接 cost + 单语生成 cost (从句首累加)
    %pn 连接 cost + 单语生成 cost ( 该词条独自的, %pw + %pC)
    %pb 最优路径时输出为 *, 其他路径为 ' '
    %pP 周边概率 (仅在 -l2 选项指定的时候有效) )
    %pA alpha, forward log 概率(仅在 -l2 选项指定的时候有效)
    %pB beta, backward log 概率(仅在 -l2 选项指定的时候有效)
    %pl 词条的表层字符串长度, 等同于 strlen (%m)
    %pL 词条的表层字符串长度,但包括空白字符串, 等同于strlen(%M)
    %phl 上文 id
    %phr 下文 id
    %f[N] csv 格式的素性中第 N 个要素
    %f[N1,N2,N3...] 第N1,N2,N3个素性, 用"," 分隔
    %FC[N1,N2,N3...] 第N1,N2,N3个素性, 用C 分隔 .
    只是ただし, 要素为空的时候, 以后的省略. (例)F-[0,1,2]
    \0 \a \b \t \n \v \f \r \\ 普通的转义字符
    \s ' ' (半角空格)
    設定ファイルに記述するときに使用

    从 dicrc 中选择的例子

    ; yomi
    node-format-yomi = %pS%f[7]
    unk-format-yomi = %M
    eos-format-yomi  = \n
    
    ; simple
    node-format-simple = %m\t%F-[0,1,2,3]\n
    eos-format-simple  = EOS\n
    
    ; csv
    node-format-csv = %m,%f[7],%f[8],%f[6],%F-[0,1,2,3],%f[4],%f[5]\n
    unk-format-csv  = %m,%m,%m,%f[6],%F-[0,1,2,3],,\n
    eos-format-csv  = EOS,,,,,,\n
    
    ; ChaSen
    node-format-chasen = %m\t%f[7]\t%f[6]\t%F-[0,1,2,3]\t%f[4]\t%f[5]\n
    unk-format-chasen  = %m\t%m\t%m\t%F-[0,1,2,3]\t\t\n
    eos-format-chasen  = EOS\n

    五、词性 ID 的定义

    posid.html

    概要

    MeCab 对于输出的特征(词性)可以对应到任意的数值 ID. 通常, 特征表现为字符串形式, 不是面向机器处理的,转换为数值ID后, 便于机器处理。

    一个特征对应于哪个 ID, 用户可以自由的定义。

    配置文件

    修改发布词典的目录中的 pos-id.def 文件.

    每行对应于一个映射规则,每个模式按如下格式书写.

    匹配模式  ID

    按这种形式书写后, 从文件头开始查找映射规则,第一个匹配的规则将被使用.

    匹配模式使用简单的正则表达式,

    • *: 匹配所有的字符串
    • (AB|CD|EF): 匹配 AB 或 CD 或 EF
    • AB: 只匹配字符串 AB

    pos-id 修改后, 需要重新编译生成词典.

    /usr/local/libexec/mecab/mecab-dict-index

    词性 ID 参照

    可以使用输出格式中的宏 %h 来引用词性的 id .

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

    另外, 库函数中可以使用 mecab_node_t::posid 引用词性的 id.

    mecab_t *mecab;
    mecab_node_t *node;
    
    mecab = mecab_new2("");
    node = mecab_sparse_tonode(mecab, "今日もしないとね");
    for (; node; node = node->next) {
      fwrite (node->surface, sizeof(char), node->length, stdout);
      printf("%d\n", node->posid)
    }

    pos-id.def 的例子

    简单的例子

    名詞 1
    動詞 2
    形容詞 3
    副詞 4
    助詞 5
    接続詞 6
    助動詞 7
    連体詞 8
    感動詞 9
    * 10

    稍微复杂的例子

    名詞 1
    (助詞|助動詞) 2
    (副詞|形容詞|連体詞) 3
    * 4

    更复杂的例子

    その他,間投,*,* 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

    六、软性的逐词分隔输出

    soft.html

    概要

    MeCab 从0.90 开始带有软性的逐词分隔输出的功能. 软性的逐词分隔提供了输出全切分中所有词条的周边概率的功能. 在全文检索的索引中使用该功能,可以解决复合词切分歧义的问题。

    ソフトわかち書き 的详细说明请参考这篇 论文.

    全切分的输出

    MeCab 缺省只输出最优解. 指定 -a 选项将不再输出最优解, 而是输出全切分 .

    % mecab -a
    東京都庁
    東京    名詞,固有名詞,地域,一般,*,*,東京,トウキョウ,トーキョー
    東      名詞,固有名詞,一般,*,*,*,東,ヒガシ,ヒガシ
    東      名詞,固有名詞,地域,一般,*,*,東,ヒガシ,ヒガシ
    東      名詞,固有名詞,人名,名,*,*,東,ヒガシ,ヒガシ
    東      名詞,固有名詞,人名,姓,*,*,東,アズマ,アズマ
    東      名詞,一般,*,*,*,*,東,ヒガシ,ヒガシ
    京都    名詞,固有名詞,一般,*,*,*,京都,キョウト,キョート
    京都    名詞,固有名詞,地域,一般,*,*,京都,キョウト,キョート
    京      名詞,固有名詞,地域,一般,*,*,京,キョウ,キョー
    京      名詞,固有名詞,人名,名,*,*,京,ミヤコ,ミヤコ
    京      名詞,固有名詞,人名,姓,*,*,京,キョウ,キョー
    都庁    名詞,一般,*,*,*,*,都庁,トチョウ,トチョー
    都      名詞,接尾,地域,*,*,*,都,ト,ト
    都      名詞,固有名詞,地域,一般,*,*,都,ミヤコ,ミヤコ
    都      名詞,固有名詞,人名,姓,*,*,都,ミヤコ,ミヤコ
    都      名詞,固有名詞,人名,名,*,*,都,ミヤコ,ミヤコ
    都      名詞,一般,*,*,*,*,都,ト,ト
    庁      名詞,接尾,一般,*,*,*,庁,チョウ,チョー
    庁      名詞,固有名詞,地域,一般,*,*,庁,チョウ,チョー
    庁      名詞,一般,*,*,*,*,庁,チョウ,チョー
    EOS

    周边概率的计算和输出

    各词条的周边概率(即该词条在输入的句子中以多大的概率出现)的计算, -a 和 -l2 选项同时使用。 -l 选项指定构建 word-lattice 的信息的级别程度, -l2 表示使用 forwardbackward 算法计算周边概率。 -l 选项可以指定如下级别:

    • -l 0: 只输出最优解 (缺省, 高速)
    • -l 1: 输出 N-best 解 (中速)
    • -l 2: ソフトわかち書きが可能なレベル (低速)

    -l2 选项由于引入了浮点数计算, 比 -l0 要慢8倍.

    各词条的周边概率的输出, 可以使用 -F 选项指定特定的输出格式, 通常使用 %pP %pb 即可。 输出格式的指定方法请参考 这里.

    • %pP: 周边概率
    • %pb: 最优解标上 "*", 其他为空
    % mecab -l2 -a -F"%m %H %pP %pb\n" -E"EOS\n"
    京都大学
    京都大学 名詞,固有名詞,組織,*,*,*,京都大学,キョウトダイガク,キョートダイガク 0.559944 *
    京都大 名詞,固有名詞,組織,*,*,*,京都大,キョウトダイ,キョートダイ 0.073824  
    京都 名詞,固有名詞,一般,*,*,*,京都,キョウト,キョート 0.004990  
    京都 名詞,固有名詞,地域,一般,*,*,京都,キョウト,キョート 0.360982  
    京 名詞,固有名詞,地域,一般,*,*,京,キョウ,キョー 0.000161  
    京 名詞,固有名詞,人名,名,*,*,京,ミヤコ,ミヤコ 0.000003  
    京 名詞,固有名詞,人名,姓,*,*,京,キョウ,キョー 0.000096  
    都 名詞,接尾,地域,*,*,*,都,ト,ト 0.000166  
    都 名詞,固有名詞,地域,一般,*,*,都,ミヤコ,ミヤコ 0.000001  
    都 名詞,固有名詞,人名,姓,*,*,都,ミヤコ,ミヤコ 0.000006  
    都 名詞,固有名詞,人名,名,*,*,都,ミヤコ,ミヤコ 0.000072  
    都 名詞,一般,*,*,*,*,都,ト,ト 0.000015  
    大学 名詞,固有名詞,地域,一般,*,*,大学,ダイガク,ダイガク 0.004919  
    大学 名詞,固有名詞,人名,名,*,*,大学,ダイガク,ダイガク 0.004441  
    大学 名詞,一般,*,*,*,*,大学,ダイガク,ダイガク 0.350523  
    大 名詞,接尾,一般,*,*,*,大,ダイ,ダイ 0.003603  
    大 接頭詞,名詞接続,*,*,*,*,大,ダイ,ダイ 0.001123  
    大 接頭詞,動詞接続,*,*,*,*,大,オオ,オー 0.000011  
    大 名詞,固有名詞,地域,一般,*,*,大,オオ,オー 0.000171  
    大 名詞,固有名詞,人名,名,*,*,大,マサル,マサル 0.000016  
    大 名詞,一般,*,*,*,*,大,ダイ,ダイ 0.001424  
    学 名詞,接尾,一般,*,*,*,学,ガク,ガク 0.067828  
    学 名詞,固有名詞,地域,一般,*,*,学,ガク,ガク 0.001092  
    学 名詞,固有名詞,人名,名,*,*,学,マナブ,マナブ 0.004203  
    学 名詞,一般,*,*,*,*,学,ガク,ガク 0.007051
    EOS

    通过指定 -t 数値 选项, 可以修改概率的平滑程度。小的数值在平滑概率的时候, 短的词条概率值会变大。 使用大的数值时, 最优解的概率变大。 缺省值为 0.75.

    % mecab -l2 -a -F"%m %H %pP %pb\n" -t0.1 -E"EOS\n"
    京都大学
    京都大学 名詞,固有名詞,組織,*,*,*,京都大学,キョウトダイガク,キョートダイガク 0.023617 *
    京都大 名詞,固有名詞,組織,*,*,*,京都大,キョウトダイ,キョートダイ 0.052790  
    京都 名詞,固有名詞,一般,*,*,*,京都,キョウト,キョート 0.113576  
    京都 名詞,固有名詞,地域,一般,*,*,京都,キョウト,キョート 0.200919  
    京 名詞,固有名詞,地域,一般,*,*,京,キョウ,キョー 0.206514  
    京 名詞,固有名詞,人名,名,*,*,京,ミヤコ,ミヤコ 0.157030  
    京 名詞,固有名詞,人名,姓,*,*,京,キョウ,キョー 0.245554  
    都 名詞,接尾,地域,*,*,*,都,ト,ト 0.168921  
    都 名詞,固有名詞,地域,一般,*,*,都,ミヤコ,ミヤコ 0.090030  
    都 名詞,固有名詞,人名,姓,*,*,都,ミヤコ,ミヤコ 0.098721  
    都 名詞,固有名詞,人名,名,*,*,都,ミヤコ,ミヤコ 0.120077  
    都 名詞,一般,*,*,*,*,都,ト,ト 0.131348  
    大学 名詞,固有名詞,地域,一般,*,*,大学,ダイガク,ダイガク 0.056029  
    大学 名詞,固有名詞,人名,名,*,*,大学,ダイガク,ダイガク 0.063926  
    大学 名詞,一般,*,*,*,*,大学,ダイガク,ダイガク 0.097919  
    大 名詞,接尾,一般,*,*,*,大,ダイ,ダイ 0.150510  
    大 接頭詞,名詞接続,*,*,*,*,大,ダイ,ダイ 0.151888  
    大 接頭詞,動詞接続,*,*,*,*,大,オオ,オー 0.083163  
    大 名詞,固有名詞,地域,一般,*,*,大,オオ,オー 0.101090  
    大 名詞,固有名詞,人名,名,*,*,大,マサル,マサル 0.090363  
    大 名詞,一般,*,*,*,*,大,ダイ,ダイ 0.128706  
    学 名詞,接尾,一般,*,*,*,学,ガク,ガク 0.233658  
    学 名詞,固有名詞,地域,一般,*,*,学,ガク,ガク 0.150100  
    学 名詞,固有名詞,人名,名,*,*,学,マナブ,マナブ 0.174424  
    学 名詞,一般,*,*,*,*,学,ガク,ガク 0.200327 
    EOS

    库函数调用

    -a 选项被指定的时候, mecab_sparse_tonode 返回的 node 节点是包含全切分的链表, -l2 指定后, mecab_node_t::prob 将被赋值为周边概率.

    mecab_t *mecab;
    mecab_node_t *node;
    
    mecab = mecab_new2("-l2 -a");
    
    node = mecab_sparse_tonode(mecab, input);
    for (; node; node = node->next) {
      /* 最適解もしくは確率が 0.05 以上のとき出力 */
      if (node->isbest || node->prob >= 0.05)  {
        fwrite (node->surface, sizeof(char), node->length, stdout);
        printf("\t%s\t%f\n", node->feature, node->prob);
      }
    }

    七、未登陆词处理的定义

    unk.html

    概要

    关于未登陆词处理(词典中不包含的词条的词法解析处理)的重定义

    配置文件

    发布词典的目录中包含 char.def 和 unk.def 文件, 修改这两个文件进行配置 .

    char.def

    未登陆词处理的规则. 请参看这里.

    unk.def

    未登陆词对应的词性表. 请参看这里.

    案例研究

    把连续的数字处理为一个词条

    • 词典 (*.csv 文件)中删除数字词条。 ipadic 的场合, 从Noun.number.csv 中删除罗马数字的词条。
    • 如下修改 char.def, 把连续的数字处理为一个未登陆词数。
      ..
      NUMERIC        1 1 0
      ..
    • 修改 unk.def , 把数字的 cost 值 改为小值。 如下, 把第4个字段的 cost 值设置为小于0的小值。
      NUMERIC,1204,1204,0,名詞,数,*,*,*,*,*
    • 编译生成词典.
      % /usr/local/libexec/mecab/mecab-dict-index

    ASCII 字符串, 仅在空格/制表符处切分 (和kakasi相同)

    • 从词典 (*.csv 文件)中把 包含ASCII 字符串的词条删除
    • 修改 char.def, 把除去空格, 符号的字符映射为同一类。同时检查其他的定义条目, 确保 0x0021..0x007E 这个范围中的字符不要映射到其他字符种类。
      ASCII       1 1 0
      
      0x0021..0x007E ASCII
    • 修改 unk.def, 把 ASCII 的 cost 值改小。 把第4个字段的 cost 值设置为小于0的小值。
      ASCII,1192,1192,0,名詞,サ変接続,*,*,*,*,*
    • 编译生成词典.
      % /usr/local/libexec/mecab/mecab-dict-index

    八、从原始词典/语料库做参数估计

    learn.html

    概要

    Mecab 可以从训练语料库中训练模型的参数(cost 值). MeCab 自身不依赖于某一个具体的词性体系的, 能够从特定的的词性体系,词典和语料库生成解析器. 参数估计使用 Conditinoal Random Fields (CRF) .

    处理流程

    下图按数据流排序.

    mecab2

    参数估计中包括以下子任务.

    以下按顺序逐个介绍.

    准备Seed 词典

    MeCab 的词典是 CSV 格式的. Seed 词典和用于发布的词典的格式基本上是相同的 .

    以下是词典的词条条目的例子 .

    進学校,0,0,0,名詞,一般,*,*,*,*,進学校,シンガクコウ,シンガクコー
    梅暦,0,0,0,名詞,一般,*,*,*,*,梅暦,ウメゴヨミ,ウメゴヨミ
    気圧,0,0,0,名詞,一般,*,*,*,*,気圧,キアツ,キアツ
    水中翼船,0,0,0,名詞,一般,*,*,*,*,水中翼船,スイチュウヨクセン,スイチューヨクセン

    前面4个字段是必须的,

    • 表层形(词条本身)
    • 左连接状态号
    • 右连接状态号
    • cost

    左连接状态号, 右连接状态号, cost 这三个字段在seed词典中无需使用, 因此全设为值0.

    第5个字段以后称为 "特征" . MeCab 为了提高通用性, 把"词性", "活用", "读音", "发音" 等和词条相关的信息无区别的都处理成 "特征". 用户可以指定多个特征, 但是特征的排列必须是固定的(例如, 第5个字段为词性, 第6个字段为词性细分类等). 通常, 特征按排列的顺序进行编号。

    特征内部使用数组的方式实现, 特征引用的时候分别称为第0个特征, 第一个特征, ... 特征的编号和内部表现(词性, 读音等) 由用户自行管理。

    以上的例子为 ipadic 中的例子, 特征列表如下定义,

    • 词性
    • 词性细分类1
    • 词性细分类2
    • 词性细分类3
    • 活用型
    • 活用形
    • 基本形
    • 读音
    • 发音

    MeCab 没有对词语的活用进行处理. 对于活用的词条, 用户必须事先逐个展开添加.

    連れ出す,0,0,0,動詞,自立,*,*,五段・サ行,基本形,連れ出す,ツレダス,ツレダス
    連れ出さ,0,0,0,動詞,自立,*,*,五段・サ行,未然形,連れ出す,ツレダサ,ツレダサ
    連れ出そ,0,0,0,動詞,自立,*,*,五段・サ行,未然ウ接続,連れ出す,ツレダソ,ツレダソ
    連れ出し,0,0,0,動詞,自立,*,*,五段・サ行,連用形,連れ出す,ツレダシ,ツレダシ
    連れ出せ,0,0,0,動詞,自立,*,*,五段・サ行,仮定形,連れ出す,ツレダセ,ツレダセ
    連れ出せ,0,0,0,動詞,自立,*,*,五段・サ行,命令e,連れ出す,ツレダセ,ツレダセ
    連れ出しゃ,0,0,0,動詞,自立,*,*,五段・サ行,仮定縮約1,連れ出す,ツレダシャ,ツレダシャ

    准备配置文件

    dicrc

    该文件中设定词典的各种动作的,以下为最小配置 .

    cost-factor = 800
    bos-feature = BOS/EOS,*,*,*,*,*,*,*,*
    eval-size = 6
    unk-eval-size = 4
    config-charset = EUC-JP
    • cost-factor: 变换为cost 值的时候的比例因子, 设置为700~800 之间是没问题的.
    • bos-feature: 句首,句尾的特征, CSV 格式.
    • eval-size: 对于已登陆词, 特征组从左开始,有eval-size 个是匹配的时候, 就认为特征标注是正确的. 通常, 已登陆词的词性、活用等信息标注正确即可,"读音"、"发音" 等特征忽略. 上面的例子中, eval-size 为6, 表示参与性能评价的包括 IPA 词性体系的词性,词性细分类 1,2,3, 活用型, 活用形这6个特征。
    • unk-eval-size: 对于未登陆词, 特征组从左开始,有unk-eval-size 个是匹配的时候, 就认为特征标注是正确的.
    • config-charset: dicrc, char.def, unk.def, pos-id.def 这几个文件的编码.
    char.def

    定义未登陆词处理的文件. 通常日语的词法分析是基于字符的种类处理未登陆词, Mecab 中哪个文字属于哪个字符种类, 用户可以进行细致的指定; 对于每个字符类别, 需要采用哪种未登陆词的识别处理,也可以进行详细的定义。

    文件的最开始是类别名的定义, 以及每个类别所采取的未登陆词识别的动作的定义.

    类别名      动作 Timing(0/1)  Grouping(0/1)  长度(0,1, 2... n)
    • 类别名: 字符类别名.
      HIRANA, KATAKANA.. 等类别的定义. DEFAULT 和 SPACE 两个类别是必须的.
    • 动作 Timing :
      在该类别中, 何时启动未登陆词处理.
    • 0: 包含已登陆词的时候, 不启动未登陆词处理
    • 1: 总是启动未登陆词处理
    • Grouping: 未登陆词候选的生成方法.
      • 0: 不合并同类型的字符.
      • 1: 合并同类型的字符.
    • 长度: 未登陆词候选的生成方法.
      • 1: 长度不超过1的字符串设置为未登陆词.
      • 2: 长度不超过2的字符串设置为未登陆词.
        ...
      • n: 长度不超过n的字符串设置为未登陆词.

    可以同时设定 Grouping 和 "长度".

    KANJI          0 0 2
    SYMBOL         1 1 0
    NUMERIC        1 1 0
    ALPHA          1 1 0
    HIRAGANA       0 1 2

    下面定义每个字符种类包含 UCS2 编码中的哪些码点(codepoint) .

    codepoint 缺省类型名 互换类型名1  互换类型名2 ..
    low_codepoint..high_codepoint  缺省类型名 互换类型名1  互换类型名2 ..

    0x0009 SPACE
    0x30A1..0x30FF  KATAKANA
    0x30FC          KATAKANA HIRAGANA  # ー

    每个码点为 UCS2(Unicode) 中以 0x 开始的16进制数.

    第一个类别是该码点的缺省字符类别, 同时, 可以在随后列举可转换的类别. 上例中, 长音几号"ー"的缺省类别是片假名,平假名为可互换类别。做 Grouping 操作的时候互换类别可当作同一群组处理.

    以下为 char.def 的具体例子.

    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 0
    KANJINUMERIC   1 1 0
    GREEK          1 1 0
    CYRILLIC       1 1 0
    
    # SPACE
    0x0020 SPACE  # DO NOT REMOVE THIS LINE,  0x0020 is reserved for SPACE
    0x00D0 SPACE
    0x0009 SPACE
    0x000B SPACE
    0x000A SPACE
    
    # ASCII
    0x0021..0x002F SYMBOL
    0x0030..0x0039 NUMERIC
    
    ... 
    
    # KATAKANA
    0x30A1..0x30FF  KATAKANA
    0x31F0..0x31FF  KATAKANA  # Small KU .. Small RO
    0x30FC          KATAKANA HIRAGANA  # ー
    unk.def

    用于未登陆词的词典.

    DEFAULT,0,0,0,記号,一般,*,*,*,*,*
    SPACE,0,0,0,記号,空白,*,*,*,*,*
    KANJI,0,0,0,名詞,一般,*,*,*,*,*
    KANJI,0,0,0,名詞,サ変接続,*,*,*,*,*
    HIRAGANA,0,0,名詞,一般,*,*,*,*,*
    HIRAGANA,0,0,0,名詞,サ変接続,*,*,*,*,*
    HIRAGANA,0,0,0,名詞,固有名詞,地域,一般,*,*,*
    ...

    该词典的词条的表层部分为 char.def 中定义的各个字符类别, 文件中定义了各类别所能指定的特征列, 为一个字符类别定义多个特征列是没问题的, 使用语料训练之后, 将自动补上适当的 cost 值。

    rewrite.def

    定义从特征列到内部状态特征列的转换映射.

    CRF使用 unigram, 上文 bigram, 下文 bigram 3个信息进行统计计算.例如, 以"美しい川"(意思:美丽的河流)为例, 使用了unigram特征(特征列来自词典的定义), 上文特征(该词条左侧开始的特征), 下文特征(该词条右侧开始的特征), rewrite.def 定义了从词典的特征到内部使用特征的映射。

    mecab1

    具体来说,是要实现类似以下功能的映射函数

    • "来る","くる" 这两个词归一化为 "来る"后,进行统计算数.
    • 计算连接 cost 的时候, 该定义指明使用特征列中的哪些部分(例如, 只使用词性, 进行词汇化操作(lexicalize) 等等)

    rewrite.def 包括3个部分.

    • [unigram rewrite]: Unigram 内部状态的映射
    • [left rewrite]: 上文bigram 映射
    • [right rewrite]: 下文bigram 映射

    每个部分后包行的每行都是一个映射规则, 映射规则结构如下

    匹配模式  变换结果

    映射规则从头开始按顺序查找, 首个匹配的规则将被使用.

    匹配模式使用简单的正则表达式,

    • *: 匹配所有的字符串
    • (AB|CD|EF): 匹配 AB 或 CD 或 EF
    • AB: 只匹配字符串 AB

    变换结果中可以使用宏 $1 $2, $3.. 表示特征中的各要素(特征的要素以 CSV 格式表示)。

    [unigram rewrite]
    # 除去发音, 使用词性1,2,3,4,活用形,活用型,原形,读音
    *,*,*,*,*,*,*,*  $1,$2,$3,$4,$5,$6,$7,$8
    # 没有读音的时候忽略
    *,*,*,*,*,*,*    $1,$2,$3,$4,$5,$6,$7,*
    
    [left rewrite]
    (助詞|助動詞),*,*,*,*,*,(ない|無い)    $1,$2,$3,$4,$5,$6,無い
    (助詞|助動詞),終助詞,*,*,*,*,(よ|ヨ)   $1,$2,$3,$4,$5,$6,よ
    ...
    
    [right rewrite]
    (助詞|助動詞),*,*,*,*,*,(ない|無い)    $1,$2,$3,$4,$5,$6,無い
    (助詞|助動詞),終助詞,*,*,*,*,(よ|ヨ)   $1,$2,$3,$4,$5,$6,よ
    ..
    feature.def

    该文件中定义了从内部状态的素生列中抽取 CRF的素生列的模板

    每行对应一个模板, 以 UNIGRAM 开头的行表示用于 UNIGRAM 的模板, 以 BIGRAM 开头的行是用于连接的模板

    每个模板中可以使用如下的宏,

    • %F[n]: 展开unigram 的第n个特征
    • %F?[n] :展开unigram 的第n个特征, 如果未定义,则该模板不使用该特征
    • %t: 展开字符类别信息. 字符类别在 char.def 文件中定义.(%t 仅在 unigram 特征的时候有效)
    • %L[n]: 展开上文语境的第 n 个特征
    • %L?[n]: 展开上文语境的第 n 个特征, 如果未定义,则该模板不使用该特征
    • %R[n]: 展开下文语境的第 n 个特征
    • %R?[n]: 展开下文语境的第 n 个特征, 如果未定义,则该模板不使用该特征

    UNIGRAM W0:%F[6]
    UNIGRAM W1:%F[0]/%F[6]
    UNIGRAM W2:%F[0],%F?[1]/%F[6]
    UNIGRAM W3:%F[0],%F[1],%F?[2]/%F[6]
    UNIGRAM W4:%F[0],%F[1],%F[2],%F?[3]/%F[6]
    
    UNIGRAM T0:%t
    UNIGRAM T1:%F[0]/%t
    UNIGRAM T2:%F[0],%F?[1]/%t
    UNIGRAM T3:%F[0],%F[1],%F?[2]/%t
    UNIGRAM T4:%F[0],%F[1],%F[2],%F?[3]/%t
    
    BIGRAM B00:%L[0]/%R[0]
    BIGRAM B01:%L[0],%L?[1]/%R[0]
    BIGRAM B02:%L[0]/%R[0],%R?[1]
    BIGRAM B03:%L[0]/%R[0],%R[1],%R?[2]
    BIGRAM B04:%L[0],%L?[1]/%R[0],%R[1],%R?[2]
    BIGRAM B05:%L[0]/%R[0],%R[1],%R[2],%R?[3]
    BIGRAM B06:%L[0],%L?[1]/%R[0],%R[1],%R[2],%R?[3]
    ...

    准备训练语料

    用于训练的数据, 和 MeCab 的输出是相同格式的,

    太郎    名詞,固有名詞,人名,名,*,*,太郎,タロウ,タロー
    は      助詞,係助詞,*,*,*,*,は,ハ,ワ
    花子    名詞,固有名詞,人名,名,*,*,花子,ハナコ,ハナコ
    が      助詞,格助詞,一般,*,*,*, が,ガ,ガ
    好き    名詞,形容動詞語幹,*,*,*,*, 好き,スキ,スキ
    だ      助動詞,*,*,*, 特殊・ダ,基本形,だ,ダ,ダ
    .       記号,句点,*,*,*,*, . , . , . 
    EOS
    焼酎    名詞,一般,*,*,*,*,焼酎,ショウチュウ,ショーチュー
    好き    名詞,形容動詞語幹,*,*,*,*,好き,スキ,スキ
    の      助詞,連体化,*,*,*,*, の,ノ,ノ
    親父    名詞,一般,*,*,*,*,親父,オヤジ,オヤジ
    .       記号,句点,*,*,*,*, . , . , . 
    EOS
    ...

    Tab 键分隔的第一个部分为词条表层文字, 随后是CSV 格式的特征列, 句子结束标志为 只包行EOS的行.

    生成训练用的二进制词典

    当前的共走目录为 WORK, 在该目录中创建子目录 seed, final

    cd $WORK
    mkdir seed final

    seed 目录中复制先前描述过的如下文件内容,

    • seed 词典(CSV 格式文件集合)
    • 所有的配置文件 (char.def, unk.def, rewrite.def, feature.def)
    • 训练用的数据 (文件名: corpus)

    % cd $WORK/seed
    % ls 
    Adj.csv          Interjection.csv   Noun.name.csv    Noun.verbal.csv  Symbol.csv        rewrite.def
    Adnominal.csv    Noun.adjv.csv      Noun.number.csv  Others.csv       Verb.csv          unk.def
    Adverb.csv       Noun.adverbal.csv  Noun.org.csv     Postp-col.csv    char.def
    Auxil.csv        Noun.csv           Noun.others.csv  Postp.csv        corpus
    Conjunction.csv  Noun.demonst.csv   Noun.place.csv   Prefix.csv       dicrc
    Filler.csv       Noun.nai.csv       Noun.proper.csv  Suffix.csv       feature.def

    运行以下命令, 生成学习用的二进制词典,

    % cd $WORK/seed
    % /usr/local/libexec/mecab/mecab-dict-index
    
    也可以通过 -d,  -o 选项指定输入输出目录来运行该命令
    % /usr/local/libexec/mecab/mecab-dict-index -d $WORK/seed -o $WORK/seed
    • -d: 包含seed 词典和配置文件的目录(缺省为当前目录)
    • -o: 训练用二进制词典的输出目录(缺省为当前目录)

    CRF 参数训练

    % cd $WORK/seed
    % /usr/local/libexec/mecab/mecab-cost-train -c 1.0 corpus model
    
    可以使用 -d 参数指定使用的词典
    % /usr/local/libexec/mecab/mecab-cost-train -d $WORK/seed -c 1.0 $WORK/seed/corpus $WORK/seed/model
    • -d: 包含训练用二进制词典的目录(缺省为当前目录)
    • -c: CRF的超参数(hyper-parameter)
    • -f: 特征频率的阈值
    • -p NUM: 实行NUM个并行训练 (缺省为1)
    • corpus: 训练数据文件名
    • model: 输出CRF参数文件名

    mecab-cost-train 在生成二进制词典的时候要消耗大量内存, 如下,使用另一个进程来生成二进制词典,可以降低内存消耗

    % /usr/local/libexec/mecab/mecab-cost-train -y -c 1.0 corpus model
    % /usr/local/libexec/mecab/mecab-cost-train -b model.txt model

    超参数 C 用于控制训练过程中学习的强度,增大 C 值, 会加强模型和训练数据的拟合,有可能产生过学习。 减小 C 值, 可以避免过学习, 但有可能学习不充分. 合适的 C 值, 只能通过交叉验证等模型选择方法来发现, 缺省值设置为 1.0。

    -f 可以用来指定特征频率的阈值,例如, -f3 表示只使用训练数据中频率高于3 的特征。合适的 C 值需要通过交叉验证等模型选择方法来发现.

    训练过程中, 将输出如下信息,

    reading corpus ... adding virtual node: 名詞,固有名詞,地域,一般,*,*,東日,トウニチ,トウニチ
    adding virtual node: 副詞,助詞類接続,*,*,*,*,かなり,カナリ,カナリ
    
    Number of sentences: 32
    Number of features:  47547
    eta:                 0.00010
    freq:                1
    C(sigma^2):          1.00000
    
    iter=0 err=1.00000 F=0.41186 target=1691.68869 diff=1.00000
    iter=1 err=1.00000 F=0.68727 target=1077.14848 diff=0.36327
    iter=2 err=0.87500 F=0.81904 target=621.20311 diff=0.42329
    iter=3 err=0.81250 F=0.86354 target=384.72432 diff=0.38068
    iter=4 err=0.68750 F=0.93685 target=233.72722 diff=0.39248
    ..
    • adding virtual node: 未知語処理を行なっても処理できなかった 形態素で, 学習の際便宜的に追加される形態素です.
    • iter: 训练次数
    • err: 句子级别的正确率
    • F: F値(正确率和召回率的调和平均)
    • target: 目标函数的值. 该值收敛的时候,训练结束
    • diff: 目标函数的相对差值, 该值小于 0.0001 的时候, 训练结束 す.

    生成用于发布的词典

    % cd $WORK/seed
    % /usr/local/libexec/mecab/mecab-dict-gen -o ../final -m model
    
    如下,可以使用 -d -o 选项指定词典
    % /usr/local/libexec/mecab/mecab-dict-gen -o $WORK/final -d $WORK/seed -m $WORK/seed/model
    • -d: 包含seed 词典和配置文件的目录(缺省为当前目录)
    • -o: 用于发布的词典的输出目录
    • -m: CRF 的参数文件

    用于发布的词典, 必须输出到和 seed 词典不同的目录,通常把包含发布词典的 final 目录打包之后用于发布 .

    生成用于解析器的词典

    % cd $WORK/final
    % /usr/local/libexec/mecab/mecab-dict-index 
    
    如下,可以使用 -d -o 选项指定词典
    % /usr/local/libexec/mecab/mecab-dict-index -d $WORK/final -o $WORK/final
    • -d: 包含seed 词典和配置文件的目录(缺省为当前目录)
    • -o: 用于解析器的二进制词典的输出目录(缺省为当前目录)

    使用生成的词典进行实际的词法解析如下.

    % mecab -d $WORK/final
    焼酎好きの親父. 
    焼酎    名詞,一般,*,*,*,*,焼酎,ショウチュウ,ショーチュー
    好き    名詞,形容動詞語幹,*,*,*,*,好き, スキ, スキ
    の      助詞,連体化,*,*,*,*,の,ノ,ノ
    親父    名詞,一般,*,*,*,*,親父, オヤジ, オヤジ
    .       記号,句点,*,*,*,*,.,.,. 
    EOS

    评价

    准备测试数据, 测试数据格式和 MeCab 的输出格式相同 .

    首先, 使用 mecab-test-gen 从测试语料(test) 中生成用于测试的句子,

    % /usr/local/libexec/mecab/mecab-test-gen < test > test.sen

    用先前生成的词典解析 test.sen .

    % mecab -d $WORK/final test.sen > test.result

    运行评价脚本 mecab-system-eval , 第一个参数为系统运行的结果, 第二个参数为正确的结果

    % /usr/local/libexec/mecab/mecab-system-eval test.result test
                        precision          recall              F
    LEVEL 0:    98.6887(647112/655710) 98.9793(647112/653785) 98.8338
    LEVEL 1:    98.2163(644014/655710) 98.5055(644014/653785) 98.3607
    LEVEL 2:    97.2230(637501/655710) 97.5093(637501/653785) 97.3659
    LEVEL 4:    96.8367(634968/655710) 97.1218(634968/653785) 96.9791

    使用 -l 选项可以指定基于哪些级别的特征进行性能评价.

    • -l 0: 只基于第0个特征进行评价
    • -l 4: 基于第0~4 个特征进行评价
    • -l -1: 基于所有的特征进行评价
    • -l "0 1 4" 分别基于第0个, 第0~1个, 0~4个特征, 进行3次评价.
    • -l "0 1 -1" 分别基于第0个, 第0~1个, 以及所有的特征, 进行3次评价.

    九、脚本语言绑定

    bindings.html

    概要

    Mecab 可以为以下脚本语言 (perlrubypythonJava) 提供调用接口. 这些语言的接口绑定都是通过 SWIG 自动生成的。 SWIG 支持的其他语言的接口也是可以生成的, 目前, 作者只为以上4种语言生成了调用接口。

    安装

    各种语言的绑定的安装方法参见 perl/README, ruby/README, python/README, java/README .

    生成 MeCab::Tagger 类的实例, 调用 parse(或者 parseToString) 方法, 解析结果以字符串形式获取。 Mecab::Tagger 的构造函数的参数,和基本的 mecab 命令运行的时候的参数时一样的, 都以字符串形式提供。

    perl
    use MeCab;
    $m = new MeCab::Tagger ("-Ochasen");
    print $m->parse ("今日もしないとね");
    ruby
    require 'MeCab'
    m = MeCab::Tagger.new ("-Ochasen")
    print m.parse ("今日もしないとね")
    python
    import sys
    import MeCab
    m = MeCab.Tagger ("-Ochasen")
    print m.parse ("今日もしないとね")
    Java
    import org.chasen.mecab.Tagger;
    import org.chasen.mecab.Node
     public static void main(String[] argv) {
     Tagger tagger = new Tagger ("-Ochasen");
     System.out.println (tagger.parse ("太郎は二郎にこの本を渡した.")); 
    }

    各词条详细信息的获取

    调用 MeCab::Tagger 类的 parseToNode 方法, 可以获取到句首这个特别词条 (为 Mecab::Node 类的实例).

    MeCab::Node 为双向链表, 可以通过 next, prev 访问下一个和前一个词条, 前后词条也都是 Mecab::Node 类型的实例。 逐次调用 next 就可以获得句子中所有的词条。

    Mecab::Node 对应的 C 语言的接口为 mecba_node_t, mecab_node_t 中包含的几乎所有成员变量都可以被访问, 但是, 通过 surface 成员返回词条的字符串时需要修改。

    以下为 perl 的例子. 该例中, 各个词条顺次访问,输出各词条的表层字符串, 词性,到该词条为止累计的 cost 值.

    use MeCab;
    my $m = new MeCab::Tagger ("");
    
    for (my $n = $m->parseToNode ("今日もしないとね"); $n ; $n = $n->{next}) {
       printf ("%s\t%s\t%d\n",
            $n->{surface},          # 表层
            $n->{feature},          # 当前词性現在の品詞
            $n->{cost}              # 到当前词累计的 cost 值
        );
    }

    错误处理

    如果在构造函数和解析途中发生错误,将抛出异常 RuntimeError, 关于异常的处理, 请参看各语言的参考手册。 以下以 python 为例说明,

    try:
        m = MeCab.Tagger ("-d .")
        print m.parse ("今日もしないとね")
    except RuntimeError, e:
        print "RuntimeError:", e;

    注意事项

    句首,句尾词条

    parseToNode 的返回值为"句首"这个特殊的词条节点, 为 Mecab::Node 类的实例, 另外请注意, "句尾"这一特殊节点也是存在的. 如果想忽视这些个节点, 如下使用 next 可以略过.

    my $n = $m->parseToNode ("今日もしないとね"); 
    $n = $n->{next}; # 丢弃 "句首"
    
    while ($n->{next}) { # 检查 next 
      printf ("%s\n", $n->{surface});
      $n = $n->{next}; # 下一节点
    }
    所有方法

    以下是 SWIG用的接口文件的一部分。使用C++ 语法描述, 用其他语言实现的时候,请相应的转换。 请参考下例添加各个方法.

    namespace MeCab {
    
      class Tagger {
    
         // 解析str, 以字符串返回结果. len 为 str 的长度(可省略)
         string parse(string str, int len);
    
         // 等同于 parse
         string parseToString(string str, int len);
    
         // 解析 str, 返回 MeCab::Node 类型的词条节点. 
         // 该词条为句首节点, 使用 next 顺序访问所有的词条节点
         Node parseToNode(string str, int len);
    
         // parse 的 Nbest 版. N 指定 nbest 的个数
         // 要使用该功能, 启动时需要指定 -l 1 选项
         string parseNBest(int N, string str, int len);
    
         // 解析结果中, 按顺序取得n-best 中近似正确的结果的时候,需要调用该函数进行初始化.
         bool  parseNBestInit(string str, int len);
    
         // parseNbestInit() 函数调用之后, 顺序调用该函数取得n-best 中近似正确的结果
         string next();
    
         // 和 next() 相同, 指示返回的是  MeCab::Node 类型
         Node  nextNode();
      };
    
      #define MECAB_NOR_NODE  0
      #define MECAB_UNK_NODE  1
      #define MECAB_BOS_NODE  2
      #define MECAB_EOS_NODE  3
    
      struct Node {
    
        struct Node  prev;  // 前一个词条节点的指针
        struct Node  next;  // 后一个词条节点的指针
    
        struct Node  enext; // 同一位置结束的词条的指针 
        struct Node  bnext; // 同一位置开始的词条的指针 
    
        string surface;             // 词条对应的字符串内容
    
        string feature;             // CSV 格式的特征
        unsigned int   length;      // 词条的长度 
        unsigned int   rlength;     // 词条的长度(包含开头的空白字符)
        unsigned int   id;          // 词条被赋予的唯一 ID 
        unsigned short rcAttr;      // 上文语境 id 
        unsigned short lcAttr;      // 下文语境 id
        unsigned short posid;       // 词条 ID (未使用)
        unsigned char  char_type;   // 字符类别信息
        unsigned char  stat;        // 词条的种类, 可取以下值
                                    // #define MECAB_NOR_NODE  0
                                    // #define MECAB_UNK_NODE  1
                                    // #define MECAB_BOS_NODE  2
                                    // #define MECAB_EOS_NODE  3
        unsigned char  isbest;      // 最有解为 1, 其他为 0
    
        float          alpha;       // forward backward 的 forward log 概率
        float          beta;        // forward backward 的 backward log 概率
        float          prob;        // 周边概率
                                    // alpha, beta, prob 只有在 -l 2 指定的时候才有定义
    
        short          wcost;       // 词条生成 cost
        long           cost;        // 累计 cost 
      };
    }

    样例程序

    请参考这些语言的样例程序 perl/test.pl, ruby/test.rb, python/test.py, java/test.java


    十、文档翻译

    Rick JIN <zhihuijin@gmail.com >

    日语水平有限, 翻译不准确之处请多谅解。

作者 rickjin

《日文分词器 Mecab 文档》有33条评论
  1. 我很喜欢自然语言处理,也经常阅读你的文章,请问可以把你的文章转发到新浪的微刊里么?微刊文章最下面有个摘录地址会指向原始链接

    [回复]

    rickjin 回复:

    sorry, 忘了回复你了, 我写的所有文章都欢迎自由转载

    [回复]

  2. mecab针对数字分词为什么只能识别全角数字?刚刚接触,不太理解,这是与词库有关还是mecab自身有限制?

    [回复]

  3. 楼主,上面的源码下载地址失效了。可以去http://taku910.github.io/mecab/下载,我今天刚刚下下来的.
    多谢楼主分享了这么多好用的东东

    [回复]

    52nlp 回复:

    嗯,我在mecab-chinese上备份了一份

    [回复]

    虾米 回复:

    我想下载mecab的源码,到哪里下载呢?

    [回复]

  4. 博主:
    你好!我初学日语,想通过日文小说积累语汇,但分词很难,找到此介绍,正符合要求,但博主及GOOGLE上均无法下载,想请博主提供Windows 版,发到1652408955@qq.com。或有效下载地址。
    谢谢!

    [回复]

  5. 博主:
    你好!我初学日语,想通过日文小说积累语汇,但分词很难,找到此介绍,正符合要求,但博文链接及GOOGLE上均无法下载,想请博主提供Windows 版,发到1652408955@qq.com。或有效下载地址。
    谢谢!

    [回复]

    52nlp 回复:

    我在Mecab-Chinese项目中留了一个备份,可以看这里 https://github.com/panyang/MeCab-Chinese/tree/master/mecab

    [回复]

    乐渭川 回复:

    谢谢提供,我还是通过GOOGLE下载到了WINDOWS版,但不会高级语言,如何输入一个TXT文本及输出一个分词文本(阅读用的)。能否提供一个模板,过份的请求。
    再次致谢!

    [回复]

    陈柏杨 回复:

    您好,关于perl 使用mecab有没有更具体的讲解,这个mecab模块是怎么安装的

  6. 文章里面关于mecab_node_t 数据结构的描述,少了两个mecab_path_t类型的变量: rpath, lpath, 另外length, rlength的类型有误,原source是unsigned short类型。哎,搞得在用python ctypes写包装python接口时,折腾了半天,怕后面也有类似折腾,特此提出。我查的源码是0.996 版

    [回复]

  7. 各种脚本语言接口绑定(perl/ruby/python/java/C#)
    翻遍这个文章并没有发现有C#相关的资料。

    [回复]

  8. 你好,我是在cygwin上安装mecab的时候,在make install这一步,出现
    collect2: error: ld returned 1 exit status
    make[2]: *** [Makefile:375:libmecab.la] 错误 1

    不知道有没有其它人碰到过?

    [回复]

    52nlp 回复:

    google了一下,有篇日文文档讲得是cygwin下安装mecab的事情,貌似有你这个错误:

    http://www.mk-mode.com/octopress/2012/03/06/06002013/

    不懂日语,貌似和下面这个配置有关,你自己再看看吧:
    また、configure 時に “CPPFLAGS=-DNOMINMAX LIBS=-liconv” のオプションを付加しない方法もありますすが、当方の場合、インストールできても文字コードの部分でバランスが取れませんでした。(入力文字列とMeCab側の文字で文字コードが一致しない!)

    [回复]

    feelins 回复:

    谢谢你!我也搜到这一段,可能还是配置的原因,我看看能不能找人翻译一下,理解一下。

    [回复]

  9. 你好!我在centos6.4上完成了mecab的编译和安装,在shell里面能够正确运行,也编译和安装了mecab-python的包,但是在python里面,出现了下面的错误。

    ---------------------------------------------------------------------------
    ImportError Traceback (most recent call last)
    in ()
    ----> 1 import MeCab

    /home/hadoop/anaconda2/lib/python2.7/site-packages/MeCab.py in ()
    24 fp.close()
    25 return _mod
    ---> 26 _MeCab = swig_import_helper()
    27 del swig_import_helper
    28 else:

    /home/hadoop/anaconda2/lib/python2.7/site-packages/MeCab.py in swig_import_helper()
    20 if fp is not None:
    21 try:
    ---> 22 _mod = imp.load_module('_MeCab', fp, pathname, description)
    23 finally:
    24 fp.close()

    ImportError: /usr/local/lib/libmecab.so.2: undefined symbol: libiconv

    libiconv-1.14.tar.gz我已经编译安装了,不知道是什么原因造成的

    [回复]

    52nlp 回复:

    可以参考这篇:https://www.52nlp.cn/mecab%E5%AE%89%E8%A3%85%E8%BF%87%E7%A8%8B%E4%B8%AD%E7%9A%84%E4%B8%80%E4%BA%9B%E5%9D%91

    19 return _MeCab
    20 if fp is not None:

    ImportError: libmecab.so.2: cannot open shared object file: No such file or directory

    In [2]:

    又google了一下这个Error:

    "ImportError: libmecab.so.2: cannot open shared object file: No such file or directory"

    又一次在一个日本网页里找到了答案,执行 “sudo ldconfig” 即可。这一次,终于Mecab可以在python中正常导入了。

    [回复]

  10. 您好我是一位日语学习者,请问,mecab可以对日文篇章,句子,词语 ,表情分别尽情词频统计吗?

    [回复]

    52nlp 回复:

    不可以,这个你要自己写程序统计了

    [回复]

  11. 您好,我在使用MeCab 来做日语和韩语的分词工作,已经分别安装了两个语种的词典包,目录分别是
    /usr/local/lib/mecab/dic/mecab-ko-dic 和 /usr/local/lib/mecab/dic/ipadic ,请问像我这种情况怎么调用对应的词典?

    现在是这么调用的,貌似不对
    韩语:Tagger tagger = new StandardTagger("/usr/local/lib/mecab/dic/mecab-ko-dic");
    日语:Tagger tagger = new StandardTagger("/usr/local/lib/mecab/dic/ipadic");

    [回复]

    永超 回复:

    我使用的是开源java 代码 cmecab-java ,操作系统是 linux ,目前单语种分词是好的,想了解怎么支持多语种分词。

    [回复]

    52nlp 回复:

    Java不太清楚,Python初始化的时候指定词典路径即可。

    [回复]

    永超 回复:

    谢谢~

    [回复]

  12. 请问如果只是想把日文某个词条简单粗暴的加到用户自定义的词典中,不管其词性等等问题的话,该怎么加入该词条?

    [回复]

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注