科普文:敏感词和贝叶斯中文分词

 分词概叙

南京市长江大桥欢迎你原来还可以这样念

        南京市长江大桥欢迎您:分词

  • 南京市  长江  大桥 欢迎 您
  • 南京  市长 江大桥  欢迎 您

一、英文分词

        1、根据空格拆分单词(split)
        2、删除停止词
        3、提取词干(最终得到特征的一步token、 term)

        例子:Machine learning is a field of computer science that uses statistical techniques to givecomputer systems the ability to learn from data.

        步骤1:英文由标点符号、空格、单词组成,所以只用根据空格和标点符号将词语分开。

        步骤2:删除停止词
                停止词: stop words
                在英文中 is a of that to with from

        使用频率比较高的词语,实际上是冠词、介词、连词,如果将这些词语都放入模型当中进行训练,那么会大幅度影响模型的训练效率。

        (machine, learning, field, computer, science, uses, statistical, techniques, systems,ability, learn, data}

        步骤3:提取词干

        这一步主要针对的是西方语言来说的(英语、拉丁语、法语等等)。用以上例子来说,learning, learn包含了相同的词干learn,所以在大多数提取特征的方法中,会将learn和learning合并为- -一个term。(machine, learn, field, computer, science, use, statistical, technique, system, ability,data }

二、中文分词

        和英文不同,中文语句是由连续的字符组成序列后呈现的,没有像英文一样的分隔符,所以相对来说,中文分词要稍微的困难一些。

        一句没有标点符号的句子,如果加上的标点符号不同,意思千差万别。
        例子: 无米面也可无鸡鸭也可无鱼肉也可无银钱也可
                无米面也可,无鸡鸭也可,无鱼肉也可,无银钱也可。
                无米,面也可;无鸡,鸭也可;无鱼,肉也可;无银,钱也可。

        对于目前的中文分词来说,许多网络用语也比较难以分割。不明|觉|厉。

        中文分词:jieba分词、IK分词
        1、基于trie树结构实现高效的词图扫描,生成句子中汉字所有可能成词情况所构成的有向无环图DAG;
        2、采用动态规划查找最大路径、找出基于词频的最大切分组合。
        3、对于未登录词,采用了基于HMM或者最大熵模型等来实现分词。

NLP-中文、英文分词_英文分词csdn-CSDN博客

        分词(tokenization,也叫word segmentation)是一种操作,它按照特定需求,把文本切分成一个字符串序列(其元素一般称为token,或者叫词语)。对于西方屈折语的文本 ,词与词之间有空格之类的显式标志指示词的边界,但是有些固定搭配仍然需要当作一个词;而对于很多孤立语和黏着语 (如汉语、日语、越南语、藏语等) ,词与词之间没有空格,因此自动分词问题就成了计算机处理孤立语和黏着语文本时面临的首要基础性工作 。

        中文分词(Chinese Word Segmentation) 指的是将一个汉字序列切分成一个个单独的词。分词就是将连续的字序列按照一定的规范重新组合成词序列的过程。中文分词还要考虑简体和繁体。

NLP(二):浅谈分词 - 知乎

分词中涉及到三个基本问题:分词规范、歧义切分和未登录词的识别。

分词规范

        “词是什么”(词的抽象定义)及“什么是 词”(词的具体界定),这两个基本问题有点飘忽不定,迄今拿不出一 个公认的、具有权威性的词表来。主要困难出自两个方面:一方面是单 字词与词素之间的划界,另一方面是词与短语(词组)的划界。

歧义切分

        歧义字段在汉语文本中普遍存在,处理这类问题可能需要进行复杂的上下文语义分析,甚至韵律分析(语气、重音、停顿等)。

例如:“结合成” 
一种切分为:(a)结合│成;另一种切分为:(b)结│合成

未登录词问题

        未登录词又称为生词(unknown word),可以有两种解释:一是指 已有的词表中没有收录的词;二是指已有的训练语料中未曾出现过的词,又称之为集外词(out of vocabulary,OOV)。

分词方法

方法典型算法特点
基于词典的方法正向最大匹配法(从左到右);
逆向最大匹配法(从右到左);最小切分(每一句中切出的词数最小)双向最大匹配(进行从左到右、从右到左两次扫描)
本质上就是字符串匹配的方法,将一串文本中的文字片段和已有的词典进行匹配,如果匹配到,则此文字片段就作为一个分词结果。但是基于词典的机械切分会遇到多种问题,最为常见的包括歧义切分问题和未登录词问题
基于统计的方法N-最短路径方法、基于词的n元语法模型的分词方法、由字构词的汉语分词方法、基于词感知机算法的汉语分词方法、基于字的生成式模型和区分式模型相结合的汉语分词方法等基于统计的分词方法是在给定大量已经分词的文本的前提下,利用统计机器学习模型学习词语切分的规律(称为训练),从而实现对未知文本的切分。例如最大概率分词方法和最大熵分词方法等
基于深度学习的分词方法LSTM+CRF、BiLSTM+CRF等深度学习方法为分词技术带来了新的思路,直接以最基本的向量化原子特征作为输入,经过多层非线性变换,输出层就可以很好的预测当前字的标记或下一个动作。在深度学习的框架下,仍然可以采用基于子序列标注的方式,或基于转移的方式,以及半马尔科夫条件随机场。

贝叶斯中文分词基本原理

参考:https://www.cnblogs.com/xlturing/p/8467021.html

贝叶斯公式

        提到基于N-Gram的概率分词方法,首先我们就要说下伟大的贝叶斯理论,说到贝叶斯理论,先说贝叶斯公式:


        贝叶斯公式也是概率论当中的基础,这里我们不再赘述,推荐一篇文章数学之美番外篇:平凡而又神奇的贝叶斯方法,讲的很不错。下面我们主要关注下在分词当中怎么利用贝叶斯原理。

分词中的贝叶斯

        我们知道通常P(Y)是一个常数,所以在使用贝叶斯公式的时候我们更多用如下的公式:


        当贝叶斯理论应用在离散数据集上的时候,可以使用频率作为概率来进行计算,在分词算法中,在给定训练语料库中,我们以词为单位进行统计,统计出每个词出现的频率,当出现一句待切分的句子时,我们将所有可能的分词结果统计出来,计算概率最大的作为切分结果。用形式化的语言描述下:
        假设训练数据集为

,其中词典集为D,

        为长度N的句子中的第i个词,那么一句话的联合概率可以表示为:


        也就是说句子当中的每个词的概率都是一个依赖于其前面所有词的条件概率。说到这里我们就是惯用套路,显然这东东没法计算,那怎么办呢,那就是贝叶斯理论中常用的,做些条件独立假设呗,这也就是所谓n-gram中n的由来。

  • 1-gram(unigram), 一元模型,句子中的每个词都是相互独立的,那么上面的公式可以简化如下:

  • 2-gram(bigram),二元模型,句子中的每个词仅仅依赖于其前面的一个词:

  • 3-gram(trigram),三元模型,句子中的每个词依赖于其前面两个词:

        一般来说,我们最多只看到前两个词,有研究表明,大于4个以上的模型并不会取得更好的效果(显然n越大,我们需要找寻n元组的词出现的频率就越低,会很直接的导致数据稀疏问题),通常情况下我们使用的是2-gram模型居多。

2-gram分词举例

        假设待切分语句为:“研究生物学”,我们要怎样进行切分呢,直观的讲我们可以看出就这么一句简单的话包含了“研究”、“研究生”、“生物”、“生物学”多个词语,那么直观上我们有如下几种切分方式:

  • 研究/生物学
  • 研究生/物/学
  • 研究/生物/学
  • 研/究/生/物/学

        我们将这些切法构建为一幅有向无环图,结点为词语,边为条件概率


(摘自[4])
        那么根据最大似然原理,我们分词的过程转为了在图中求解最佳路径的问题,我们只需要选取任意一种搜索算法,例如在结巴分词中是利用动态规划找寻最大概率路径。

1-gram实例

        上面说了那么多,还是上code比较有干货,我们以1-gram为例,来进行一个阐述,这里我们主要参考了结巴分词。在实现的过程中涉及到的核心问题:建立前缀字典树、根据句子建立DAG(有向无环图)、利用动态规划得到最大概率路径。

建立前缀字典树

代码如下:

        with open(dict_path, "rb") as f:
            count = 0
            for line in f:
                try:
                    line = line.strip().decode('utf-8')
                    word, freq = line.split()[:2]
                    freq = int(freq)
                    self.wfreq[word] = freq
                    for idx in range(len(word)):
                        wfrag = word[:idx + 1]
                        if wfrag not in self.wfreq:
                            self.wfreq[wfrag] = 0  # trie: record char in word path
                    self.total += freq
                    count += 1
                except Exception as e:
                    print("%s add error!" % line)
                    print(e)
                    continue

        我们利用dict来建立这颗前缀字典树,遇到一个词时,会将词路径当中所有的子串都记录在字典树中。(其实这种方式存放是有大量冗余子串的,不过查询是会更加方便)

建立DAG

代码如下:

    def get_DAG(self, sentence):
        DAG = {}
        N = len(sentence)
        for k in range(N):
            tmplist = []
            i = k
            frag = sentence[k]
            while i < N and frag in self.wfreq:
                if self.wfreq[frag]:
                    tmplist.append(i)
                i += 1
                frag = sentence[k:i + 1]
            if not tmplist:
                tmplist.append(k)
            DAG[k] = tmplist
        return DAG

        因为在载入词典的时候已经将word和word的所有前缀加入了词典,所以一旦frag not in wfreq,即可以断定frag和以frag为前缀的词不在词典里,可以跳出循环。

利用动态规划得到最大概率路径

        值得注意的是,DAG的每个结点,都是带权的,对于在词典里面的词语,其权重为其词频,即wfreq[word]。我们要求得route = (w1, w2, w3 ,.., wn),使得Σweight(wi)最大。

动态规划求解法

        满足dp的条件有两个

  • 重复子问题
  • 最优子结构

        我们来分析最大概率路径问题。

重复子问题
        对于结点Wi和其可能存在的多个后继Wj和Wk,有:

  1. 任意通过Wi到达Wj的路径的权重为该路径通过Wi的路径权重加上Wj的权重{Ri->j} = {Ri + weight(j)} ;
  2. 任意通过Wi到达Wk的路径的权重为该路径通过Wi的路径权重加上Wk的权重{Ri->k} = {Ri + weight(k)} ;

最优子结构
        对于整个句子的最优路径Rmax和一个末端节点Wx,对于其可能存在的多个前驱Wi,Wj,Wk…,设到达Wi,Wj,Wk的最大路径分别为Rmaxi,Rmaxj,Rmaxk,有:
Rmax = max(Rmaxi,Rmaxj,Rmaxk…) + weight(Wx)
        于是问题转化为:
求Rmaxi, Rmaxj, Rmaxk…
        组成了最优子结构,子结构里面的最优解是全局的最优解的一部分。
        很容易写出其状态转移方程:
Rmax = max{(Rmaxi,Rmaxj,Rmaxk…) + weight(Wx)}

代码

代码如下:

    def get_route(self, DAG, sentence, route):
        N = len(sentence)
        route[N] = (0, 0)
        logtotal = log(self.total)
        for idx in range(N - 1, -1, -1):
            route[idx] = max((log(self.wfreq.get(sentence[idx:x + 1]) or 1) -
                              logtotal + route[x + 1][0], x) for x in DAG[idx])

        这里值得注意的是在求频率时,使用了log函数,将除法变成了减法,防止溢出。

完整代码

        对于句子“我是中国人”,我们可以看到如下图所示的效果:


        我将完整的代码放在了git上,这里的词典用的就是结巴分词中的词典,其中好多代码都是从结巴分词复用过来的,大家需要可以瞅瞅:
machine-learning-journey/seg_ngram at master · xlturing/machine-learning-journey · GitHub

参考文献

  1. 数学之美番外篇:平凡而又神奇的贝叶斯方法
  2. 自然语言处理中的N-Gram模型详解
  3. 概率语言模型的分词方法
  4. 《统计自然语言处理》宗成庆
  5. 结巴分词python
  6. jieba分词学习笔记(三)

敏感词

        敏感词是网站的特殊敏感词。大部分网站等,为了方便管理,都进行了关于敏感词的设定。

        在多数网站,敏感词一般是指带有敏感政治倾向(或反执政党倾向)、暴力倾向、不健康色彩的词或不文明语,也有一些网站根据自身实际情况,设定一些只适用于本网站的特殊敏感词。

新华社发布禁用的“敏感词”和准确用语

新华社公布第一批禁用词:报道领导活动不得用“亲自”_西藏自治区公安厅

新华社新闻报道中的禁用词 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

01Byte空间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值