自然语言处理——中文文本预处理

近期,在自学自然语言处理,初次接触NLP觉得十分的难,各种概念和算法,而且也没有很强的编程基础,学着稍微有点吃力。不过经过两个星期的学习,已经掌握了一些简单的中文、英文语料的预处理操作。写点笔记,记录一下学习的过程。

1、中文语料的特点

  第一点:中文语料中词与词之间是紧密相连的,这一点不同与英文或者其它语种的语料,因此在分词的时候不能像英文使用空格分词,可以jieba库进行分词。第二点:编码问题,中文语料的编码格式是unicode,而不是utf-8编码格式。这里介绍utf-8编码和unicode编码读取的区别,unicode 一个中文字符占2个字节,而UTF-8一个中文字符占3个字节,因此如果不注意编码问题,在处理过程中肯定会出错的。

2、中文语料预处理

  本次我做的中文语料预处理包含了以下操作:数据导入、数据清洗、中文分词、去停用词、特征处理(TF-IDF权重计算)。下面我将模块介绍我的处理过程。

2.1 数据导入

  首先,先准备好本次要使用的数据集,一段摘自腾讯体育新闻中新闻报道,文本保存格式设置为utf-8。然后倒入进python,使用到open函数读取文件,读取格式选择‘r’表示读取文件,编码encoding = ’utf-8’,查看文件中的内容用read函数。具体编码如下:

#文件读取
def read_txt (filepath):
    file = open(filepath,'r',encoding='utf-8')
    txt = file.read()
    return txt

  读取结果展示:

自然语言处理-中文语料预处理-风君雪科技博客

 (注意:返回的txt是str类型的,即字符串类型,不需要decode。str与bytes表示的是两种数据类型,str为字符串型,bytes为字节型。对str编码encode得到bytes,对bytes解码decode得到str)

2.2 数据清洗

  新闻文本数据中不仅包括了中文字符,还包括了数字、英文字符、标点等非常规字符,这些都是无意义,并且需要处理的数据。清洗的方法使用的是正则表达式,匹配规则为:[u4e00-u9fa5],un匹配n,其中n是一个用四个十六进制数字表示的Unicode字符,而4e00-9fa5之间的Unicode编码表示的是20000多个中文字符。具体编码如下:

#匹配[^u4e00-u9fa5]
def find_chinese (file):
    pattern = re.compile(r'[^u4e00-u9fa5]')
    chinese_txt = re.sub(pattern,'',file)
    return chinese_txt

  解释:[u4e00-u9fa5]表示匹配汉字,[^u4e00-u9fa5]表示匹配除汉字以外的所有字符。

  数据展示:

 2.3 中文分词

  分词是中文文本分析的重要内容,正确的分词可以帮助我们更好的构建模型、运用算法分析。中文分词一般使用jieba库中的cut方法,cut方法分词有两种模式,一种为全模式,另一种为精准模式,相较于全模式,精准模式分词更加精准可靠,因此选用精准模式对文本分词。注:精准模式和全模式的区别在于是否cut_all,精准模式选择cut_all= False;全模式选择cut_all= True。Jieba库的安装十分简单,只需要在命令框中输入:pip install jieba 即可安装。

import jieba

txt = '''
腾讯体育3月6日讯 史蒂芬-库里时隔127天后复出,勇士113-121不敌猛龙。猛龙本场比赛过后,取得44胜18负战绩,锁定季后赛,成为本赛季联盟第2支锁定季后赛的球队,第1支是雄鹿。
比赛开始后,库里持球组织进攻,明显改变了猛龙的防守,给克里斯和维金斯创造了轻松得分的机会。但在第一节还剩6分11秒下场时,库里没有得分,2次三分出手全部偏出。
但在第二节比赛重新登场后,我们看到了那个熟悉的库里。他接球投三分命中,迎着防守人超远压哨三分命中,第三节还迎着洛瑞完成3+1。那个三分之王和2次常规赛MVP风采依旧。
勇士将士就像打不死的小强,从第一节开始就非常顽强,紧紧的咬住比分,甚至伺机反扑。'''

#全模式
jieba_list = jieba.cut(txt,cut_all=True)
jieba_txt1 = ' '.join(jieba_list)
print('全模式分词:',jieba_txt1)

#精准模式
jieba_list = jieba.cut(txt, cut_all=False)
jieba_txt2 = ' ' .join(jieba_list)
print('精准模式分词:',jieba_txt2)

 通过这段代码我们可以体会一下全模式分词和精准模式分词的区别。

自然语言处理-中文语料预处理-风君雪科技博客

需要注意的一点是jieba分词后返回的数据类型是generator类型,是一个迭代器,需要使用for循环才能读取其中的内容。

  处理的算法:

#中文分词
def cut_word(text):
    # 精准模式
    jieba_list = jieba.cut(text, cut_all=False)
    return jieba_txt

 分词结果:

2.4 停用词去除

  无论是中文中,还是英文中,都有用来起连接作用的连词、虚词、语气词等无意义的词,这些词没有具体的含义,只是起着衔接句子的作用。这些词对文本分析没有任何帮助,因此我们还需要对分词后的数据做停用词处理。进行停用词处理需要停用词表,本文中选用的停用词表为哈工大的停用词表,因为在国内研究自然语言处理他们属于翘楚。

停用词表在我的博客中有分享,有需要的可以自行下载。具体代码如下:

#去除停用词
def seg_sentence(list_txt):
    #读取停用词表
    stopwords = stopwords = read_txt('哈工大停用词表.txt')
    seg_txt = [ w for w in list_txt if w not in stopwords]
    return seg_txt

去停用词结果:

自然语言处理-中文语料预处理-风君雪科技博客

2.5 词频统计

   统计分词后文本的词频,目的是找出对文本影响最重要的词汇,统计这些词的频率可以帮助我们了解文章重点强调了什么,以及方便后续构建模型。词频统计中使用了python自带的collections库中的counter接口,它可以帮助统计词频率。Collection安装方法:pip install collections。具体代码:

#词频统计
def counter(txt):
    seg_list = txt
    c = Counter()
    for w in seg_list:
        if w is not ' ':
            c[w] += 1
    return c

  统计结果如下所示:

自然语言处理-中文语料预处理-风君雪科技博客

 2.6 特征选择—TF-IDF权重计算

  TF-IDF权重是很可靠的权重表征方式,用以评估一字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。TF-IDF加权的各种形式常被搜索引擎应用,作为文件与用户查询之间相关程度的度量或评级。代码:

#TF_IDF计算
def tf_idf(txt):
    corpus_txt = [' '.join(txt)]
    stopword_list = read_txt(r'哈工大停用词表.txt').splitlines()
    vector = TfidfVectorizer(stop_words=stopword_list)
    tfidf = vector.fit_transform(corpus_txt)
    print(tfidf)
    # 获取词袋模型中的所有词
    wordlist = vector.get_feature_names()  
    # tf-idf矩阵 元素a[i][j]表示j词在i类文本中的tf-idf权重
    weightlist = tfidf.toarray()
    # 打印每类文本的tf-idf词语权重,第一个for遍历所有文本,第二个for便利某一类文本下的词语权重
    for i in range(len(weightlist)):
        print("-------第", i, "段文本的词语tf-idf权重------")
        for j in range(len(wordlist)):
            print(wordlist[j], weightlist[i][j]  )

运行结果:

自然语言处理-中文语料预处理-风君雪科技博客

 2.7 总结

  到此中文文本的预处理就结束了。暂时这两个星期学到的预处理就这么多,如有不当之处,还忘大佬能不吝指导。最后附上全部代码:

import nltk
import jieba
import re
from collections import Counter
from sklearn.feature_extraction.text import TfidfVectorizer

#创建去除非中文字符的函数
#数据清洗,去除标点符号,数字,等其它非中文字符
#匹配[^u4e00-u9fa5]
def find_chinese (file):
    pattern = re.compile(r'[^u4e00-u9fa5]')
    chinese_txt = re.sub(pattern,'',file)
    return chinese_txt

#文件读取
def read_txt (filepath):
    file = open(filepath,'r',encoding='utf-8')
    txt = file.read()
    return txt

#中文分词
def cut_word(text):
    # 精准模式
    jieba_list = jieba.cut(text, cut_all=False)
    return jieba_list

#去除停用词
def seg_sentence(list_txt):
    #读取停用词表
    stopwords = stopwords = read_txt('哈工大停用词表.txt')
    seg_txt = [ w for w in list_txt if w not in stopwords]
    return seg_txt

#词频统计
def counter(txt):
    seg_list = txt
    c = Counter()
    for w in seg_list:
        if w is not ' ':
            c[w] += 1
    return c
#TF_IDF计算
def tf_idf(txt):
    corpus_txt = [' '.join(txt)]
    stopword_list = read_txt(r'哈工大停用词表.txt').splitlines()
    vector = TfidfVectorizer(stop_words=stopword_list)
    tfidf = vector.fit_transform(corpus_txt)
    print(tfidf)
    # 获取词袋模型中的所有词
    wordlist = vector.get_feature_names()
    # tf-idf矩阵 元素a[i][j]表示j词在i类文本中的tf-idf权重
    weightlist = tfidf.toarray()
    # 打印每类文本的tf-idf词语权重,第一个for遍历所有文本,第二个for便利某一类文本下的词语权重
    for i in range(len(weightlist)):
        print("-------第", i, "段文本的词语tf-idf权重------")
        for j in range(len(wordlist)):
            print(wordlist[j], weightlist[i][j]  )

#主函数
if __name__ == "__main__":
    #读取文本信息
    news = read_txt('新闻(中文).txt')
    print("原文:",news)
    #清洗数据,去除无关标点
    chinese_news = find_chinese(news)
    print("原文文本:",news)
    print("纯中文文本:",chinese_news)
    #结巴分词
    chinese_cut = cut_word(chinese_news)
    print(chinese_cut)
    # 停用词去除
    chinese_sentence = seg_sentence(chinese_cut)
    print(chinese_sentence)
    #词频统计
    lists = counter(chinese_sentence)
    print(lists)
    for list in lists.most_common(20):
        print(list)
    #TF-IDF权重计算
    tf_idf(chinese_sentence)