NLP之文本预处理详解

入门小菜鸟,希望像做笔记记录自己学的东西,也希望能帮助到同样入门的人,更希望大佬们帮忙纠错啦~侵权立删。

Ps预防针:与英文分类文本预处理相比,中文分类文本预处理更加复杂关键

目录

一、进行文本预处理的原因

二、去除停用词

1、停用词

2、去除停用词

三、中文分词技术

1、分词处理的原因

2、基于词典(规则)的中文分词

3、基于统计的中文分词方法


一、进行文本预处理的原因

解决特征空间高维性、特征分布稀疏和语义相关性

毕竟计算机不是人嘛,我们的语言需要经过一定的预处理让他们可以读入以及方便后续训练分类,

接下来我们来说说文本预处理有哪些常用的方法


二、去除停用词(特征提取的一种)

1、停用词

定义:在信息检索中,为节省存储空间和提高搜索效率,在处理文本之前自动过滤掉某些字或词,这些字或词就被称为Stop Words(停用词)。

这些停用词都是人工输入、非自动化生成的,生成后的停用词会形成一个停用词表。但是,并没有一个明确的停用词表能够适用于所有的工具。

停用词的分类:

一类是人类语言中包含的没有什么实际含义的功能词,比如'the'、'is'、'at'、'which'、'on'等

问题:

但是对于搜索引擎来说,当所要搜索的短语包含功能词,特别是像'The Who'等复合名词时,去除停用词后就会导致问题。

另一类是包括应用十分广泛的但对搜索引擎无法保证能够给出真正相关的搜索结果的词汇词(比如'want'等)

2、去除停用词

首先下载一个停用词的文本文件,不过要注意看看你是中文文本还是英文文本,找好对应的停用词文本文件

以中文文本为例:(英文文本思路也相似)

(1)首先使用jieba分词对已去掉非中文的中文文本数据进行分词

首先你要进行相关库(jieba)的导入

import jieba

去除非中文数据

先设定好去除非中文的数据的存储文件名/路径

path1 = 'path1.txt' #去除非中文的数据存储文件名

然后以只读的形式(防止被无意改动)打开我们待处理的文本(path),以“写入”形式打开我们刚刚准备好的去除非中文的数据的存储文件(path1)

f = open(path, 'r', encoding= 'utf-8', errors= 'ignore')#path为我们待处理的文本,即原始数据集
fw = open(path1,'w', encoding='utf-8',errors= 'ignore')

注:其中将文本格式编码为utf-8,是为了防止编码错误

逐行处理,去除非中文字符

for line in f: #逐行进行处理
     constr = ''#记录每行处理后的数据
     for uchar in line:
        if uchar >= u'\u4e00' and uchar <= u'\u9fa5': #是中文字符
             if uchar != ' ': #排除空格
                 constr += uchar      
     fw.write(constr+'\n') #写入处理后的数据,每行以换行符隔开

Ps:这里如果是英文文本数据的话,我们就要去除非英文文本,处理方法如下:

for line in f: #逐行进行处理
     constr = ''#记录每行处理后的数据
     for uchar in line:
        if ord(uchar) in (97,122) or ord(uchar) in (65,90) or uchar == ' ':  #是英文字符
             constr += uchar      
     fw.write(constr+'\n') #写入处理后的数据,每行以换行符隔开

接下来是分词

读取刚刚去除非中文字符的数据

txt = open(path1, encoding="utf-8").read()  #读取刚刚去除非中文字符的数据

调用jieba进行分词

words = jieba.lcut(txt)     #分词

(2)根据停用词表,对分词后的文本去除停用词

加载停用词txt

stoppath = "xxx.txt"    #停用词txt

提取停用词

stopwords = [line.strip() for line in open(stoppath, encoding="utf-8").readlines()] #加载停用词

拓:

  • readline() 方法:用于从文件读取整行,包括 "\n" 字符。
  • strip() 方法:用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列,这里指的是去除换行符

去除停用词

sentences = " " #初始化
for word in words:
     if word not in stopwords: #不在停用词表中
         sentences += word #把有用的词留下,存在sentences里
         sentences += " " #以空格隔开

最终得到的sentences就是去除了停用词后的结果


三、中文分词技术

Ps:像上面一样直接调用像jieba那些的库进行分词也很方便,但除了调用库之外还有一些基本的算法

1、分词处理的原因

实践证明:特征粒度为词粒度远远好于字粒度

基于字粒度的算法得出的结果损失了过多的n-gram信息

中文分词技术一般分为2类:

基于词典(规则)的中文分词和基于统计的中文分词

2、基于词典(规则)的中文分词

Ps:字典需要自己去找

主要是通过人工来建立词库(词典),通过词典匹配的方式对句子进行划分。

它的实现比较简单高效,但是劣势也很明显:对未登记在词典里的词很难进行处理

总的思想:

当需要对一个句子进行分词时,首先把句子拆分成多个部分,将每个部分与字典一一对应,如果该词语在词典中,分词成功,否则继续拆分匹配直到成功。

所以这个方法思想的关键在于字典;如何切分句子;以什么样的形式顺序匹配字典中的词

常用方法如下:

(1)正向最大匹配法(FMM)

思想步骤:

按从左往右的顺序,从句子中取出m(m指词典中最长词的长度)个字作为匹配字段

查找字典,和取出的字段进行匹配

进入判断:

匹配成功:将该字段作为一个词分出去

匹配不成功:将该字段最后一个字去掉,剩下的字作为新的匹配字段,再次进行匹配

循环以上过程直到分完为止

代码实现:

#FMM正向最大匹配法
def fmm(text):
    #这里进行补充对词典进行处理
    '''''
    word是词典提出来的包含所有的词的列表
    m是词典中最长词的长度
    '''''
    result = []
    index = 0
    length = len(text)
    while length > index:
        for pointer in range(m + index ,index,-1):
            piece = text[index:pointer]
            if piece in word:
                index = pointer - 1
                break
        index = index + 1
        result.append(piece)
    return result

(2)逆向最大匹配法(RMM)

RMM的原理与FMM基本相同,不同的是分词的方向与FMM相反(也就是从右向左开始匹配扫描)

#RMM反向最大匹配法
def rmm(text):
    #这里进行补充对词典进行处理
    '''''
    word是词典提出来的包含所有的词的列表
    m是词典中最长词的长度
    '''''
    result = []
    length = len(text)
    index = length - 1
    while index < 0:
        for pointer in range(index - m ,index):
            piece = text[pointer:index]
            if piece in word:
                index = pointer + 1
                break
        index = index - 1
        result.append(piece)
    result.reverse() #进行一个反转
    return result

(3)双向最大匹配法(Bi-MM)

它将FMM和RMM两者得到的结果进行比较,然后按照最大匹配原则,选取词数切分最少的作为结果。

具体思想:

如果正反向分词结果词数不同:取分词数量少的那个

如果正反向分词结果词数不同:统计两方法结果的单字数量,返回少的那个

代码实现:

def bi_mm(text):
    result_fmm = fmm(text)
    result_rmm = rmm(text)
    if len(result_fmm) == len(result_rmm):
        if result_fmm == result_rmm:
            return result_fmm
        else:
            onewordf = len([w for w in result_fmm if len(w)== 1])
            onewordr = len([w for w in result_rmm if len(w)== 1])
            return result_fmm if onewordf < onewordr else result_rmm
    else:
        return result_fmm if len(result_fmm) < len(result_rmm) else result_rmm

3、基于统计的中文分词方法

统计学认为分词是一个概率最大化问题。

即拆分句子,基于语料库来统计相邻的字组成的词语出现的概率,相邻的词出现的次数多,即出现概率大,然后依照概率值进行分词。

步骤:

(1)需要构建语言模型

(2)对句子进行单词划分,划分的结果运用统计方法来计算概率,获取概率最大的分词方式。

(统计方法有:隐马尔可夫模型HMM,条件随机场CRF)

N-gram语言模型

假设S表示长度为N,由(W1,W2,....,WN)字序列组成的句子,则代表S的概率(即S在文本中出现的可能性)为:

P(S) = P(W1,W2,...,Wi) = P(W1) * P(W2|W1) * P(W3|W1,W2) .... P(WN|W1,W2,...,WN-1)

(因为我们总不可能把人类所有说过的话都统计一下,直接得到P(S)吧)

从公式可以看到:每个字的出现都与他之前出现过的字有关,这个计算量很大

所以这里我们可以利用马尔科夫假设——即当前词只与最多前n-1个有限的词相关

一元模型

当n = 1时:即当前词与其他词毫无关系。公式变为:

P(S) = P(W1,W2,...,Wi) = P(W1) * P(W2) * P(W3) .... P(WN)

这就是一元模型,记作uni-gram

二元模型

当n=2时:即出现在第i位上的词wi仅与它前面的一个历史词wi-1有关。

P(S) = P(W1,W2,...,Wi) = P(W1) * P(W2|W1) * P(W3|W2) .... P(WN|WN-1)

二元模型又被称为一阶马尔可夫链,记作bi-gram

总结

N元语言模型——即N-1阶马尔可夫假设

实际上最常用的是N=3的三元模型

即:P(S) = P(W1,W2,...,Wi) = P(W1) * P(W2|W1) * P(W3|W1,W2) .... P(WN|WN-2,WN-1)

注:

(1)通常为了方便计算,并且为了防止因为某个词概率为0就导致P(S)为0(往往是因为数据不够),采取log操作将乘法变为加法;

(2)由于数据缺乏,采取平滑算法

(2)其他方法(详细的分析可能有补也可能没有……懒惰使人惬意(bushiQAQ )

隐含马尔可夫模型(HMM)

它是将分词作为字在句子中的序列标注任务来实现的。

基本思路:每个字在构造一个特定词语时都占据着一个特定的位置即词位,一般采用四结构词位:B(词首),M(词中),E(词尾)和S(单独成词)

【维特比算法】


欢迎大家在评论区批评指正,谢谢啦~

你可能感兴趣的:(深度学习,NLP,自然语言处理,人工智能,nlp,文本预处理,python)