一、日文分词器 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 。同时, 平均解析速度高于 ChaSen, Juman, KAKASI 这些日文词法分析器. 顺便说一下, 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 词典
Juman 词典
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 installUTF-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.
三、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:%sn", 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: %sn", 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%sn", 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 %dn",
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 场合
首先, 把 includemecab.h, binlibmecab.dll liblibmecab.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 中不存在该函数, 估计为笔误
三、词条追加方法
概要
有两种方法可以往词典中追加词条.
- 在系统词典中追加
- 在用户词典中追加
在系统词典中追加
词典更新不频繁, 同时不想降低解析速度时,最好直接修改系统词典.
- 进入包含文件 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 开发的时候, 内存大小受限,难以使用静态展开的方法。 现在, 由于内存和硬盘的空间都很充裕, 所以使用静态展开的策略, 可以提升解析的速度。
将来会考虑加入如下框架: 自动从词法描述中把活用静态展开; 目前,活用的展开完全交给用户自己完成。
四、输出格式
概要
MeCab 和 ChaSen 一样, 可以比较自由的定义输出格式. Mecab 在配置文件中可以定义多种输出格式, 运行的时候可以切换不同的输出格式, 这是 MeCab 独有的功能。
输出格式的指定
可以修改以下4种输出格式.
- node: 输出一个词语,缺省为空
- unk: 输出一个未登陆词, 缺省使用 node 输出格式
- bos: 句首的输出 , 缺省为空
- eos: 句尾的输出, 缺省为 “EOSn”
没有显示指定输出格式的时候, 将使用缺省的格式 .
可以使用两种方式指定输出格式.
- 通过命令行指定
% 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]