分词

128 阅读8分钟

分词算法设计中的几个基本原则:

1、颗粒度越大越好:用于进行语义分析的文本分词,要求分词结果的颗粒度越大,即单词的字数越多,所能表示的含义越确切,如:“公安局长”可以分为“公安 局长”、“公安局 长”、“公安局长”都算对,但是要用于语义分析,则“公安局长”的分词结果最好(当然前提是所使用的词典中有这个词)

2、切分结果中非词典词越少越好,单字字典词数越少越好,这里的“非词典词”就是不包含在词典中的单字,而“单字字典词”指的是可以独立运用的单字,如“的”、“了”、“和”、“你”、“我”、“他”。例如:“技术和服务”,可以分为“技术 和服 务”以及“技术 和 服务”,但“务”字无法独立成词(即词典中没有),但“和”字可以单独成词(词典中要包含),因此“技术 和服 务”有1个非词典词,而“技术 和 服务”有0个非词典词,因此选用后者。

3、总体词数越少越好,在相同字数的情况下,总词数越少,说明语义单元越少,那么相对的单个语义单元的权重会越大,因此准确性会越高。

基于词典的分词算法

最大匹配法(Maximum Matching)

正向最大匹配(FMM,Forward Maximum Matching)

从左往右取词,取词最大长度为词典中长词的长度,每次右边减一个字,直到词典中存在或剩下1个单字。 例:研究生命的起源

假设字典中的相关内容如下: 研究 研究生 生命 命 的 起源 生命的起源

假定最大匹配字数设定为5

正向最大匹配过程:

研究生命的 研究生命 研究生 #第一个词匹配成功

命的起源 命的起 命的 命 #第二个词匹配成功,一个单字

的起源 的起 的 #第三个词匹配成功

起源 #第四个词匹配成功

那么正向最大匹配的结果就是: 研究生/命/的/起源

正向最大匹配算法Python代码实现

def FMM_func(user_dict, sentence):
    """
    正向最大匹配(FMM)
    :param user_dict: 词典
    :param sentence: 句子
    """
    # 词典中最长词长度
    max_len = max([len(item) for item in user_dict])
    start = 0
    while start != len(sentence):
        index = start+max_len
        if index>len(sentence):
            index = len(sentence)
        for i in range(max_len):
            if (sentence[start:index] in user_dict) or (len(sentence[start:index])==1):
                print(sentence[start:index], end='/')
                start = index
                break
            index += -1


 user_dict = ['我们', '在', '在野', '生动', '野生', '动物园', '野生动物园', '物','园','玩']
 sentence = '我们在野生动物园玩'
 FMM_func(user_dict, sentence) //  我们/在野/生动/物/园/玩/

逆向最大匹配(BMM,Backward Maximum Matching)

例:我们在野生动物园玩

假定字典中的内容如下: 我 我们 在 在野 野生 生动 动物 物 动物园 园 野生动物园 玩

词典最大长度max_len = 5

生动物园玩 动物园玩 物园玩 园玩 玩 #第一个词匹配成功,一个单字

野生动物园 #第二个词匹配成功

我们在 们在 在 #第三个词匹配成功,一个单字

我们 #第四个词匹配成功

由上可得逆向最大匹配的结果: 我们/在/野生动物园/玩

逆向最大匹配算法Python代码实现

def BMM_func(user_dict, sentence):
    """
    反向最大匹配(BMM)
    :param user_dict:词典
    :param sentence:句子
    """
    # 词典中最长词长度
    max_len = max([len(item) for item in user_dict])
    result = []
    start = len(sentence)
    while start != 0:
        index = start - max_len
        if index < 0:
            index = 0
        for i in range(max_len):
            if (sentence[index:start] in user_dict) or (len(sentence[start:index])==1):
                result.append(sentence[index:start])
                start = index
                break
            index += 1
    for i in result[::-1]:
        print(i, end='/')


 user_dict = ['我们', '在', '在野', '生动', '野生', '动物园', '野生动物园', '物','园','玩']
 sentence = '我们在野生动物园玩'
 BMM_func(user_dict, sentence)  // 我们/在/野生动物园/玩/

双向最大匹配

FMM和BMM两种算法都分词一遍,然后根据大颗粒度词越多越好,非词典词和单字词越少越好的原则,选取其中一种分词结果输出。 例:我们在野生动物园玩 正向最大匹配法,最终分词结果为:“我们/在野/生动/物/园/玩”,其中,总分词数6个,单个词为3。 逆向最大匹配法,最终分词结果为:“我们/在/野生动物园/玩”,其中,总分词数4个,单个词为2。

全切分路径选择方法

全切分方法就是将所有可能的切分组合全部列出来,并从中选择最佳的一条切分路径。关于路径的选择方式,一般有N-最短路径方法,基于词的n元语法模型方法等。   N-最短路径方法的基本思想就是将所有的切分结果组成有向无环图,每个切词结果作为一个节点,词之间的边赋予一个权重,最终找到权重和最小的一条路径作为分词结果。    基于词的n元语法模型可以看作是n最短路径方法的一种优化,不同的是,根据n元语法模型,路径构成时会考虑词的上下文关系,根据语料库的统计结果,找出构成句子最大模型概率。一般情况下,使用unigram和bigram的n元语法模型的情况较多 链接:www.zhihu.com/question/19…

基于序列标注的分词方法

针对基于词典的机械切分所面对的问题,尤其是未登录词识别,使用基于统计模型的分词方式能够取得更好的效果。基于统计模型的分词方法,简单来讲就是一个序列标注问题。   在一段文字中,我们可以将每个字按照他们在词中的位置进行标注,常用的标记有以下四个label:B,Begin,表示这个字是一个词的首字;M,Middle,表示这是一个词中间的字;E,End,表示这是一个词的尾字;S,Single,表示这是单字成词。分词的过程就是将一段字符输入模型,然后得到相应的标记序列,再根据标记序列进行分词。举例来说:“达观数据是企业大数据服务商”,经过模型后得到的理想标注序列是:“BMMESBEBMEBME”,最终还原的分词结果是“达观数据/是/企业/大数据/服务商”   在NLP领域中,解决序列标注问题的常见模型主要有HMM和CRF。 链接:www.zhihu.com/question/19…

HMM(HiddenMarkov Model,隐马尔可夫模型)

基本的思想就是根据观测值序列找到真正的隐藏状态值序列。在中文分词中,一段文字的每个字符可以看作是一个观测值,而这个字符的词位置label(BEMS)可以看作是隐藏的状态。使用HMM的分词,通过对切分语料库进行统计,可以得到模型中5大要素:起始概率矩阵,转移概率矩阵,发射概率矩阵,观察值集合,状态值集合。在概率矩阵中,起始概率矩阵表示序列第一个状态值的概率,在中文分词中,理论上M和E的概率为0。转移概率表示状态间的概率,比如B->M的概率,E->S的概率等。而发射概率是一个条件概率,表示当前这个状态下,出现某个字的概率,比如p(人|B)表示在状态为B的情况下人字的概率。 有了三个矩阵和两个集合后,HMM问题最终转化成求解隐藏状态序列最大值的问题,求解这个问题最常使用的是Viterbi算法,这是一种动态规划算法 其具体算法可参考维基词条

CRF

CRF(Conditionalrandom field,条件随机场)是用来标注和划分结构数据的概率化结构模型,通常使用在模式识别和机器学习中,在自然语言处理和图像处理等领域中得到广泛应用。和HMM类似,当对于给定的输入观测序列X和输出序列Y,CRF通过定义条件概率P(Y|X),而不是联合概率分布P(X,Y)来描述模型。 CRF算法的具体算法可参考维基百科词条 在实际应用中有很多工具包可以使用,比如CRF++,CRFsuite,SGD,Wapiti 等,其中CRF++的准确度较高。在分词中使用CRF++时,主要的工作是特征模板的配置。CRF++支持unigram,bigram两种特征,分别以U和B开头。举例来讲U00:%x[-2,0]表示第一个特征,特征取值是当前字的前方第二个字,U01:%x[-1,0]表示第二个特征,特征取值当前字前一个字,U02:%x[0,0]表示第三个特征,取当前字,以此类推。特征模板可以支持多种特征,CRF++会根据特征模板提取特征函数 链接:www.zhihu.com/question/19…

jieba

jieba分词共有三种模式:精准模式、全模式、搜索引擎模式

  1. 精准模式:jieba.lcut(字符串,cut_all=False),默认时为cut_all=False,表示为精确模型(即默认为精确模式)。精确模式是把文章词语精确的分开,并且不存在冗余词语,切分后词语总词数与文章总词数相同。
  2. 全模式:jieba.lcut(字符串,cut_all=True),其中cut_all=True表示采用全模型进行分词。全模式会把文章中有可能的词语都扫描出来,有冗余,即在文本中从不同的角度分词,变成不同的词语。
  3. 搜索引擎模式:在精确模式的基础上,对长词语再次切分。

.lcut与.cut

jieba.cut生成的是一个生成器,generator,即可以通过一个for循环来获取里面的每一个词; jieba.lcut直接生成一个list