迄今为止,前四篇文章已经介绍了分词的若干思路,其中有基于最大概率的查词典方法、基于HMM或LSTM的字标注方法等。这些都是已有的研究方法了,笔者所做的就只是总结工作而已。查词典方法和字标注各有各的好处,我一直在想,能不能给出一种只需要大规模语料来训练的无监督分词模型呢?也就是说,怎么切分,应该是由语料来决定的,跟语言本身没关系。说白了,只要足够多语料,就可以告诉我们怎么分词。

看上去很完美,可是怎么做到呢?《2.基于切分的新词发现》中提供了一种思路,但是不够彻底。那里居于切分的新词发现方法确实可以看成一种无监督分词思路,它就是用一个简单的凝固度来判断某处该不该切分。但从分词的角度来看,这样的分词系统未免太过粗糙了。因此,我一直想着怎么提高这个精度,前期得到了一些有意义的结果,但都没有得到一个完整的理论。而最近正好把这个思路补全了。因为没有查找到类似的工作,所以这算是笔者在分词方面的一点原创工作了。

语言模型 #

首先简单谈一下语言模型。

很多数据挖掘的读者都已经听说过Word2Vec,知道它是一个能够生成词向量的工具,很多人也知道将词向量作为模型的特征来进行输入。但相信不少读者不知道为什么会有词向量,为什么Word2Vec能生成词向量。Word2Vec本身的光芒(Google出品、速度快、效果也不错、在Python中有很好实现等)已经把同类产品以及背后的原理都给掩盖下去了。事实上,词向量的初衷,是为了更好地生成语言模型,最经典的文章应该是深度学习的鼻祖之一——Bengio——的《A Neural Probabilistic Language Model》。这一段的重点是谈语言模型,不是词向量。关于词向量,有兴趣的读者可以参考下面的文章:

Deep Learning in NLP (一)词向量和语言模型:
http://licstar.net/archives/328

火光摇曳的《我们是这样理解语言的》系列:
http://www.flickering.cn/?s=我们是这样理解语言的

语言模型是计算条件概率
$$p(w_n|w_1,w_2,\dots,w_{n-1})$$
的模型,其中$w_1,w_2,\dots,w_{n-1}$是句子中的前$n-1$个词(或字),$w_n$是第$n$个词(或字)。语言模型在很多方面都有应用,比如说分词、语音识别、机器翻译等。为了得到语言模型,有很多方法,比如说最简单的是“统计+平滑”的方法,还有最大熵语言模型、CRF语言模型等,而当前深度学习框架下研究得很多的是“神经网络语言模型”,它的大概思路是:$p(w_n|w_1,w_2,\dots,w_{n-1})$是关于$w_1,w_2,\dots,w_n$的一个函数,这个函数的具体形式我不知道,所以利用神经网络去拟合它,为了更好地拟合,并且减少模型参数,还把词语“嵌入”到实数空间中,用短向量来表示词语,跟语言模型一起训练。从这个角度看,词向量只是语言模型的副产品。

语言模型生成的词向量能够较好地表示语义,这是很有趣的,却也是在情理之中。什么是语义?对人类来说,语义是一种推理和理解的过程,而我们的语言模型,就是从前$n-1$个字推测下一个字,这也是一个推理的过程。既然包含了推理成分在里边,就有可能捕捉到语义了。

无监督分词 #

说语言模型似乎说得有点多了,不过,本文要介绍的分词方法,就是以“基于字的语言模型”为基础的。

我们从最大概率法出发,如果一个长度为$l$的字符串$s_1, s_2, \dots, s_l$,最优分词结果为$w_1, w_2, \dots, w_m$,那么它应该是所有切分中,概率乘积
$$p(w_1)p(w_2)\dots p(w_m)$$
最大的一个。

假如没有词表,自然也就不存在$w_1, w_2, \dots, w_m$这些词了。但是,我们可以用贝叶斯公式,将词的概率转化为字的组合概率:
$$p(w)=p(c_1)p(c_2|c_1)p(c_3|c_1 c_2)\dots p(c_k|c_1 c_2 \dots c_{k-1})$$
其中$w$是一个$k$字词,$c_1,c_2,\dots,c_k$分别是$w$的第$1,2,\dots,k$个字。可以发现,$p(c_k|c_1 c_2 \dots c_{k-1})$就是我们前面提到过的字的语言模型。

当然,对于很大的$k$,$p(c_k|c_1 c_2 \dots c_{k-1})$还是不容易估算的,不过幸好按照我们的经验,词的平均长度不会很大,因此,我们只需要用n-gram语言模型就够了,其中$n$为4时效果就挺不错了。

那分词具体又是怎么操作呢?假如字符串$s_1, s_2, s_3\dots, s_l$,如果不进行切分,那么它的路径概率应该是
$$p(s_1)p(s_2)p(s_3)\dots p(s_l)$$
如果$s_1, s_2$应该合并为一个词,那么它的路径概率是
$$p(s_1 s_2)p(s_3)\dots p(s_l)=p(s_1)p(s_2|s_1)p(s_3)\dots p(s_l)$$
如果$s_2, s_3$应该合并为一个词,那么它的路径概率是
$$p(s_1)p(s_2 s_3)\dots p(s_l)=p(s_1)p(s_2)p(s_3|s_2)\dots p(s_l)$$
如果$s_1, s_2, s_3$应该合并为一个词,那么它的路径概率是
$$p(s_1 s_2 s_3)\dots p(s_l)=p(s_1)p(s_2|s_1)p(s_3|s_1 s_2)\dots p(s_l)$$
看到特点了吗?每一种切分方式,事实上都对应着$l$个条件概率的相乘,我们就是从这些条件概率的相乘模式中,找出结果最大的那个。而同样的,如果我们知道了最优的相乘模式,就可以对应地写出分词结果来。

更系统地看,其实就是将分词转化为了标注问题,如果字语言模型取到4-gram,那么它相当于做了如下的字标注:

b:单字词或者多字词的首字
c:多字词的第二字
d:多字词的第三字
e:多字词的其余部分

对于句子中的一个字$s_k$来说,就有
$$\begin{aligned}&p(b)=p(s_k)\\
&p(c)=p(s_k|s_{k-1})\\
&p(d)=p(s_k|s_{k-2} s_{k-1})\\
&p(e)=p(s_k|s_{k-3} s_{k-2} s_{k-1})
\end{aligned}$$

这就是将分词问题变成了一种字标注问题,而每个标签的概率由语言模型给出。而且,显然b后面只能接b或者c,类似地,就得到非零的转移概率只有:
$$p(b|b),\,p(c|b),\,p(b|c),\,p(d|c),\,p(b|d),\,p(e|d),\,p(b|e),\,p(e|e)$$
这些转移概率的值,决定了划分出来的是长词还是短词。最后找最优路径,依旧由viterbi算法完成。

到这里,问题就变成了语言模型的训练了,这是无监督的。我们只需要花心思优化语言模型,而这方面不论是理论还是实战都已经很成熟了,有不少现成的工具可以用。简单地可以只用传统的“统计+平滑”模型,如果要从语义来做,那么就可以用最新的神经语言模型。总而言之,分词的效果,取决于语言模型的质量。

实践:训练 #

首先来训练语言模型。这里文本数据是50万微信公众号的文章,约2GB大小,训练语言模型用的是传统的“统计+平滑”的方法,使用kenlm这个工具来训练。

kenlm是一个C++编写的语言模型工具,具有速度快、占用内存小的特点,也提供了Python接口。首先下载编译它:

wget -O - http://kheafield.com/code/kenlm.tar.gz |tar xz 
cd kenlm
./bjam -j4
python setup.py install

接着训练语言模型。kenlm的输入很灵活,不用预先生成语料文本,而可以通过管道的方式传递。比如先编写一个p.py

import pymongo
db = pymongo.MongoClient().weixin.text_articles

for text in db.find(no_cursor_timeout=True).limit(500000):
    print ' '.join(text['text']).encode('utf-8')

我的文章放在MongoDB中,所以是上面的格式,如果你的数据放在其他地方,请做相应修改,其实很简单,就是把你要训练的文本分好词(用空格隔开,如果你是做基于字的模型,就把模型的每个字用空格隔开),然后逐一print出来。

然后就可以训练语言模型了,这里训练一个4-gram的语言模型:

python p.py|./kenlm/bin/lmplz -o 4 > weixin.arpa
./kenlm/bin/build_binary weixin.arpa weixin.klm

arpa是通用的语言模型格式,klm是kenlm定义的二进制格式,klm格式占用空间更少。最后我们就可以在Python中载入了

import kenlm
model = kenlm.Model('weixin.klm')
model.score('微 信', bos=False, eos=False)
'''
score函数输出的是对数概率,即log10(p('微 信')),其中字符串可以是gbk,也可以是utf-8
bos=False, eos=False意思是不自动添加句首和句末标记符
'''

实践:分词 #

有了上述基础,就可以来做一个分词系统了。

import kenlm
model = kenlm.Model('weixin.klm')

from math import log10

#这里的转移概率是人工总结的,总的来说,就是要降低长词的可能性。
trans = {'bb':1, 'bc':0.15, 'cb':1, 'cd':0.01, 'db':1, 'de':0.01, 'eb':1, 'ee':0.001}
trans = {i:log10(j) for i,j in trans.iteritems()}

def viterbi(nodes):
    paths = nodes[0]
    for l in range(1, len(nodes)):
        paths_ = paths
        paths = {}
        for i in nodes[l]:
            nows = {}
            for j in paths_:
                if j[-1]+i in trans:
                    nows[j+i]= paths_[j]+nodes[l][i]+trans[j[-1]+i]
            k = nows.values().index(max(nows.values()))
            paths[nows.keys()[k]] = nows.values()[k]
    return paths.keys()[paths.values().index(max(paths.values()))]

def cp(s):
    return (model.score(' '.join(s), bos=False, eos=False) - model.score(' '.join(s[:-1]), bos=False, eos=False)) or -100.0

def mycut(s):
    nodes = [{'b':cp(s[i]), 'c':cp(s[i-1:i+1]), 'd':cp(s[i-2:i+1]), 'e':cp(s[i-3:i+1])} for i in range(len(s))]
    tags = viterbi(nodes)
    words = [s[0]]
    for i in range(1, len(s)):
        if tags[i] == 'b':
            words.append(s[i])
        else:
            words[-1] += s[i]
    return words

实践:效果 #

语言模型的大小有近3G,因此就不放出来了,有需要的读者可以联系我。下面看一下一些例子。

水 是 生命 的 源泉 , 是 人类 赖以生存 且无 可 替代 的 营养 物质 。 为 使 队员们 更加 了解 水 对 生命 的 至关重要 性 , 提高 队员们 对 水 更 科学 的 认识 与 理解 , 倡导 节水 爱 水 的 环保 意识 , 青少年 环境 知识 科普 课堂 走进 大 金 小学 , 为 五、 六年级 近 300 余 名 队员 开展 了 一场 《 水 与 生命 》为主题 的 科普 知识 讲座 。 此次 活动 共分为三 场 进行 , 宣讲 人 祝 老师 结合 PPT , 图文并茂 、 生动 地 从 水 的 特性 、 水 与 生命 、 水 与 生活 以及 节水 技巧 四个 方面 与 队员们 进行 了 交流 。 祝 老师 告诉 队员们 水 对人体 的 重要 性 , 详细 说明 了 水 的 营养 组成 , 同 时 提醒 队员们 要 学会 健康 科学 的 饮水 方法 , 并 分享 了 节水 小窍门, 希望 队员们 都能 以 自己 为 榜样 , 努力 承担 “ 小 小 节水 宣传 员 ”的 职责 , 积极 带动 身边的人 一起 参与 节约用水 。 PH 试纸 检测 水 的 酸碱度 ,队员们 都 表现 了 浓厚的兴趣 , 纷纷 取了 试纸 回家 测试 水质 。 讲座 结束后, 队员们 都 领到 了 “ 小 小 节水 宣传 员 ” 培训 课 程 的 结业证书 。 从 队员们 兴奋 的 表情 中 能够 感受 到 队员们 节水 爱 水 的 决心 。 保护 水 环境 , 珍惜 水 资源 , 从点滴做起 , 从 自己 做起 , 只要 每个人都 做到 了 保护 生态 、 爱护 环境 , 那么 碧水蓝天 就会 离我们 越来越 近 ! 打赏小编 的 最好 方式 就是 —— 点赞 ↓↓ 长按二维码 , 关注 我们 吧! ↓↓

可以看到,效果还是不错的,对长词的识别效果都挺好。但是,有些情况可能不符合我们的习惯认识,比如“队员们”作为一个词了,还有“且无可替代”错误地分为了“且无 可 替代”,因为“且无”太频繁了。

区 志愿者 协会 在 前几日 得知 芦林街道 三官殿居 有 一 居民 家庭 特别 困难 的 情况 , 12月 12 日 下午 , 招募 了 7 名 志愿者 来到 芦林三官 殿周全禄 老人 家 , 送去 了 一袋大米 和 一床棉被 。   此次 助 养 慰问 品 是 由 广丰区 志愿者 协会 公益 基金 提供 , “ 暖冬行动 ” 作为 志愿者 协会 帮困 项目 的 其中 重要 一 项 , 由 参与 暖冬行动 的 志愿者们 负责 执行 发 放到 走访 核实 的 困境 家庭 手中 。 志愿者 现场 和 周 全 禄 老人 交谈 , 从 他 本人 和 周边 群众 了解 到 他的 基本 家庭 状况 , 他 本人 今年 62 岁 , 娶了一个 患有 精神 疾病 的 妻子 , 生 了 2个 儿子 , 小孩 大 的 14 岁 , 小 的 12 岁 , 妻子 在 十年 前 也 离家出走 , 至今 未 回 , 留下 他 和 2个 儿子 共同 生活 , 由于 儿子 遗传 了 母 亲 的 精神 疾病 , 大 儿子 的 种种 不 正常 表现 , 不能 在 学校 正常 上 学 , 只能 整天 跟着 小 儿子 两个 人 无所事事 , 游手好闲 , 什么 事 也 做 不 了。 周 老 本身就是 一个 老实巴交 的 农民 , 今年 不慎 干农活 时 摔了一跤 , 医药费 2万多 元 , 都是 村里 和 亲戚 邻居 帮忙 筹集 的 。 他 住的 房子 也是 亲戚 筹集 盖的 一层 瓦房 。 凌乱的 客厅 , 衣服 基本 上 就是 没有 什么 换洗 , 湿了 就 随意 搭着 晾干 , 然后 接着 穿 我们 在 他 家 看到 做的 饭菜 , 这 就是 一 家人 赖以 生存 的 厨房 。 这 就是 卧室 , 床铺被褥 都是 破旧不堪 , 我们 带去 的 一 床 新 棉被 他的 外甥女 偶尔 帮他 整理 下 卫生 , 做些家务 赠人玫瑰 , 手有余香 ; 扶困助弱 , 千古 美德 ; 能力 不 分 大 小 , 善举 不 分 先后 , 真情 重 在 付出 。 众人 拾柴火焰高 , 我们 将 把所有 爱心 力量 汇集 在 一起 , 传递 社会 大 家庭 的 温暖 , 传递 社会 正能量 , 放 飞 困境儿童 的 未来 梦想 ! 伸出 您的 双手 , 奉献 您的 爱心 , 让我们 行动 起来 , 共同 关爱 困境 家庭 , 让 所 他们 同 在 蓝天 下 健康 快乐 成长 ! 如果 您 或 您身边的 人 有 12 - 15 岁 男 孩子 的 衣物 , 棉被 等 暖冬 物质 可以 捐赠 , 请伸出您 充满 爱心 的 双手 , 给 这个 特殊 家庭 一个 暖暖的 冬日 ! !! 暖冬 物质 接收 地址: 广丰区 志愿者 协会 暖冬 物质 接收 联系人: 18 6 07 03 48 18 ( 段 先生 ) 13 8 70 32 70 03 ( 陈女士 ) 供稿: 段 建 波 图片 : 段 建 波 编辑: 周 小 飞

可以看到,即使对“拾柴火焰高”这样的长词也有不错的识别效果。当然,错误的例子也不少,比如“把所有”、“让我们”、“请伸出您”成为了一个词。

根据 业务 发展 需要 , 现 将 我 公司 20 16 年 招聘 应届高校 毕业 生 公告 如下 : 一 、 招聘 岗位 20 16 年 我 公司 拟招聘 应届高校 毕业 生 20 名 。 招聘 岗位 和 学历 、 专业 要求 见下表 。 二、 报名 条件 1. 列入国家 招生 计划 、 具备 派遣 资格 、 处于 毕业 学 年 的 全日制 普通 高等院校 在 校 生 , 以及 经 教育 部 留学 服务 中心 认证 并 具备 派遣 资 格 的 归国留学 生 ; 2. 遵守 国家 法律法规 和 学校 规章制度 , 具有 良好 的 思想 品质 和 道德 素质 , 无 刑事 犯罪 和 严重 违反 校纪校规 记录 ; 3. 专业 对 口 , 符合 工作 岗位要求 , 热爱 铁路 集装箱 事业 ; 4. 学习 成绩 优良 , 取得 相应 的 大学 本科 及以上学历 和 学位证书 ; 应聘 在 京 单位 岗位 毕业 生 需 取得 国家 大学 外语 四级考试 合格 证书 ( 主 修其他语 种 除外 ); 5. 身心 健康 , 近期 医院 健康 体检合格 , 能够 适应 应聘 岗位 工作 要求 。 三、 报名 方法 应 聘者 需 登录 " 中国 铁路 人才 招聘 网 — 个人 中心 " 栏目 按照 流程 进行 报名 应聘 ( 首次 登录 须 进行 网上 注册 )。 报名 截止日期 为 20 16 年 1月 10 日 。 每人 限报一个 岗位 。 四、 招聘 流程 1. 资格 确认 。 根据 资格审查 和 初步 筛选 情况 , 于201 6年 2月 28 日前 , 择优 以 邮件 、 短信 或 电话 方式 通知 毕业 生 参加 招聘 考试 。 2. 招聘 考试 。 参加 招聘 考试 的 毕业 生 应 携带 在 中国 铁路 人才 招聘 网 打印 的 毕业 生 应聘 登记 表 , 本人 身份证 、 学生 证、 所在 学校 盖章 的 就业 推荐 表 、 成绩 单 、 外语 证书 等 材料 的 原件及复印件 。 招聘 考试 在 20 16 年 4月 15 日前 完成 , 具体 时间 、 地点 另行通知 。 3. 人员 公示 。 拟录用人 选 将 统一 在 中国 铁路 人才 招聘 网 和 公司 官网 进行 公示 。 招聘 过程中, 对 未 进入 下一 环节 的 毕业 生 不再 另行通知 。 五、 其他 事项 1. 公司 不 委托 第三 方 招聘 , 也不 在 招聘 过程中 向 应聘者 收 任何 费用 。 2. 应聘者 的 报名 材料 概不退回 , 在 招聘 过程中 公司 对 应聘者 的 相关 信息 予以 保密 。 毕业 生 应对 招聘 各环节 所提供的 材料 的 真实 性 负责 , 凡 弄虚作假 的 , 一 经 发现 , 取消 聘用 资格 。 3. 单位 地址: 北京 市 西城区 鸭子 桥路 24 号 中 铁 商务大厦 邮政编码 : 10 00 55 联系 电话:0 10 - 51 89 27 23

总的来说 #

总的来说,这种无监督的分词方式,事实上是对我们的用字习惯做了总结,把我们常见的用字模式提取了出来。因此,它对于不少长词,尤其是固定搭配的成语,有着很好的识别效果。同时,我们也有一些频繁的用字组合,比如前面说的“让我们”之类的,也被视为单个词语了。可能我们会觉得这是一个不合理的情况,但反过来想想,既然我们经常说“让我们”,那么为什么不把“让我们”就作为一个“词”呢?

换句话说,我们做分词,事实上就是事先提取出固定的用语模式罢了,这个固定的用语模式,不一定是我们认识中的“词”,也有可能是习惯用语等。当然,这里边有个相互矛盾的地方,就是分词的粒度太细,则词表的词数不会过多,但单个句子的长度则会变长;分词的粒度太粗,则词表的词数可能暴增,但好处是单个句子的长度会减少。而本文所提供的分词方式,可以通过转移概率的调整,来实现对分词粒度的调整,以适应不同的任务。

同时,前面已经说了,分词的效果取决于语言模型的质量,这使得我们只需要优化语言模型,而且语言模型可以无监督地训练,这是一个明显的好处。比如,如果我们希望能够实现具有语义理解能力的分词模型,那么用神经网络之类的方法训练语言模型即可,如果我们考虑速度,那么传统的统计方法就不错了(用kenlm从50万文本中得到语言模型,只用了10分钟不到)。总而言之,提供了最大的自由度。

转载到请包括本文地址:https://kexue.fm/archives/3956

更详细的转载事宜请参考:《科学空间FAQ》

如果您还有什么疑惑或建议,欢迎在下方评论区继续讨论。

如果您觉得本文还不错,欢迎分享/打赏本文。打赏并非要从中获得收益,而是希望知道科学空间获得了多少读者的真心关注。当然,如果你无视它,也不会影响你的阅读。再次表示欢迎和感谢!

如果您需要引用本文,请参考:

苏剑林. (Sep. 12, 2016). 《【中文分词系列】 5. 基于语言模型的无监督分词 》[Blog post]. Retrieved from https://kexue.fm/archives/3956

@online{kexuefm-3956,
        title={【中文分词系列】 5. 基于语言模型的无监督分词},
        author={苏剑林},
        year={2016},
        month={Sep},
        url={\url{https://kexue.fm/archives/3956}},
}