中文分词
中文分词是将中文文本切分成一系列有意义的词语的过程。中文分词可以用于文本分析、机器翻译、信息检索等领域。传统的中文分词方法主要是基于规则和统计方法,其中规则分词法主要通过定义一些分词规则来分词,如基于汉字的笔画、拼音、部首等规则;而统计分词法则是通过训练大规模中文语料库来自动学习分词规律,并使用这些规律进行分词。随着深度学习技术的发展,基于神经网络的中文分词方法也逐渐得到广泛应用。在中文分词领域上,使用最多的就是jieba库,本次我也将重点介绍jieba库进行中文分词。
jieba是目前最好的Python中文分词组件,它主要有以下3种特性:
- 支持3种分词模式:精确模式、全模式、搜索引擎模式
- 支持繁体字
- 支持自定义词典
jieba.cut和jieba.lcut接受3个参数:
- 需要分词的字符串(unicode或UTF-8字符串、GBK字符串)
- cut_all参数:是否使用全模式,默认值为False
- HMM参数:用来控制是否使用HMM模型,默认值为True
jieba.cut_for_search和jieba.lcut_for_search接受2个参数:
- 需要分词的字符串(unicode或UTF-8字符串、GBK字符串)
- HMM参数:用来控制是否使用HMM模型,默认值为True
jieba.cut和jieba.cut_for_search所返回的结果是一个可迭代的generator,可使用for循环来获得分词后得到的每一个词语(unicode)。
jieba.lcut和jieba.lcut_for_search直接返回list结果。
示例:
import jieba text = '中文分词是将中文文本切分成一系列有意义的词语的过程。' print(jieba.cut(text)) print(jieba.cut_for_search(text)) print(jieba.lcut(text)) print(jieba.lcut_for_search(text))
运行结果:
['中文', '分词', '是', '将', '中文', '文本', '切', '分成', '一系列', '有', '意义', '的', '词语', '的', '过程', '。'] ['中文', '分词', '是', '将', '中文', '文文', '文本', '切分', '分成', '一系', '一系列', '系列', '列有', '有意', '意义', '的', '词语', '的', '过程', '。'] ['中文', '分词', '是', '将', '中文', '文本', '切', '分成', '一系', '系列', '一系列', '有', '意义', '的', '词语', '的', '过程', '。']
<generator object Tokenizer.cut at 0x000001B56EE14740> <generator object Tokenizer.cut_for_search at 0x000001B56EE14740> ['中文', '分词', '是', '将', '中文', '文本', '切', '分成', '一系列', '有', '意义', '的', '词语', '的', '过程', '。'] ['中文', '分词', '是', '将', '中文', '文本', '切', '分成', '一系', '系列', '一系列', '有', '意义', '的', '词语', '的', '过程', '。']
分词默认使用的是精确模式,我们可以通过cut_all参数来改为全模式。如果想使用搜索引擎模式的话,使用jieba.lcut_for_search即可。
示例
import jieba text = '中文分词是将中文文本切分成一系列有意义的词语的过程。' print(jieba.lcut(text,cut_all=False)) # 默认精确模式,即cut_all=False print(jieba.lcut(text,cut_all=True)) # 改为全模式,即cut_all=True print(jieba.lcut_for_search(text)) # 搜索引擎模式
运行结果:
['中文', '分词', '是', '将', '中文', '文本', '切', '分成', '一系列', '有', '意义', '的', '词语', '的', '过程', '。'] ['中文', '分词', '是', '将', '中文', '文文', '文本', '切分', '分成', '一系', '一系列', '系列', '列有', '有意', '意义', '的', '词语', '的', '过程', '。'] ['中文', '分词', '是', '将', '中文', '文本', '切', '分成', '一系', '系列', '一系列', '有', '意义', '的', '词语', '的', '过程', '。']
自定义分词词典
很多时候使用jieba分词的结果会不尽人意,它的分词里如果没有该词语,就会将其分开,比如下面的示例:
import jieba text = '艾派森是创新办主任也是大数据专家' jieba.lcut(text)
['艾派', '森是', '创新', '办', '主任', '也', '是', '大', '数据', '专家']
可以发现,我们是不想让它把“艾派森”、“创新办”、“大数据”分开的,但是在它的词典里是没有这些新词语的,所以这时候我们需要自定义分词词典进行补充。
方法1:直接定义词典列表
我们可以直接将我们自定义的词语放入一个列表中,然后使用jieba.load_userdict进行应用
b = ['艾派森','创新办','大数据'] jieba.load_userdict(b) # 应用自定义词典列表 jieba.lcut(text)
['艾派森', '是', '创新办', '主任', '也', '是', '大数据', '专家']
方法2:外部文件载入
外部文件的话我们需要创建一个自定义词典txt文件,里面写入自定义词语即可,注意是一行一个词语。然后将该文件的路径传入jieba.load_userdict即可,我这里是代码和文件在同一路径下,所以直接写入文件名即可。
# 方法2:外部文件载入 jieba.load_userdict('自定义词典.txt') jieba.lcut(text)
['艾派森', '是', '创新办', '主任', '也', '是', '大数据', '专家']
方法3:动态增加或删除词语
优点:比添加自定义词典灵活,随用随机。
比如当我们遇到如下情况时:
text2 = '我们中出了一个叛徒' jieba.lcut(text2)
['我们', '中出', '了', '一个', '叛徒']
可以发现它误解了我们的初始意思,将“中出”作为一个词语进行分开,我们汉语里面哪有这个词语,似乎日语里面有这个词,要么就是csgo里面的口头语“中出”,意思是从中路出了。所以这时候我们将需要“中出”进行删除,然后加入“出了”这个词语。
jieba.del_word('中出') # 删除单词 jieba.lcut(text2)
['我们', '中', '出', '了', '一个', '叛徒']
可以发现将“中出”删除后,它就不会将中和出进行组合了。
jieba.add_word('出了') jieba.lcut(text2)
['我们', '中', '出了', '一个', '叛徒']
当我们添加了“出了”词语后,它就可以将出和了进行组合了,这就符合了我们最初的意思。
问题:如果我们不想删除“中出”这个词,但是又不想让它合在一起,该怎么办?
那么我们就需要提高“中”和“出”这两个词的权重,这样就不会在进行组合了。
# 如果我们不想删除“中出”这个词,但是又不想让它合在一起,可以增大它的词频 jieba.add_word('中出') # 为了演示效果,我们需要回到最初始的样子 jieba.del_word('出了') print(jieba.lcut(text2)) # 调节词的词频,使其能(或不能)被分词 # tune=True:执行词频调整,默认False不执行 jieba.suggest_freq(('中','出'),tune=True) print(jieba.lcut(text2))
['我们', '中出', '了', '一个', '叛徒'] ['我们', '中', '出', '了', '一个', '叛徒']
可以看出,我们同样实现了前面的效果。
去除停用词
去除停用词是一种常用的自然语言处理技术,它可以大大提高文本分类、情感分析、机器翻译等任务的效果。常用的停用词包括数字、时间、标点符号、常见单词等。
在中文分词之后, 就需要进行去除停用词。
首先我们需要准确一个停用词库,这里我建议大家使用哈工大的中文停用词库,比较齐全。如果里面没有你想去除的词语,你也可以再文件里面进行添加,注意还是一行一个词语。这里我们读取停用词库文件,将里面的停用词进行存储,后面做筛选用。
with open('停用词库.txt', encoding='utf-8') as f: # 可根据需要打开停用词库,然后加上不想显示的词语 con = f.readlines() stop_words = set() # 集合可以去重 for i in con: i = i.replace("\n", "") # 去掉读取每一行数据的\n stop_words.add(i) stop_words
读取完停用词库我们将可以去除停用词了,示例如下:
text3 = '昨天我吃了一大碗米饭,真的是太好吃了!###@' result = [] for word in jieba.lcut(text3): if word not in stop_words: result.append(word) result
['昨天', '吃', '大碗', '米饭', ',', '真的', '太', '好吃', '!', '###', '@']
可以发现它确实去除了停用词库中的词语,但是还有标点符号等字符没有去除。
所以这里我们还需要使用re正则进行去除字符,只保留中文。
# 去除一些无用的字符只提取出中文出来 import re new_text = "".join(re.findall('[\u4e00-\u9fa5]+', text3, re.S)) new_text
'昨天我吃了一大碗米饭真的是太好吃了'
可以发现经过这一步处理,我们原来语句中的标点符号等异常字符都去除掉了。此时再进行分词和去停用词就可以算完美了。
result = [] for word in jieba.lcut(new_text): if word not in stop_words: result.append(word) result
['昨天', '吃', '大碗', '米饭', '真的', '太', '好吃']
可以看出分词的结果都只有中文了。
如果我们不想保留单词,比如上面的“吃”、“太”,这样的词语没有什么价值,只需要在去重的时候加上过滤条件即可。原理就是判断字的长度是否大于1。
result = [] for word in jieba.lcut(new_text): if word not in stop_words and len(word) > 1: result.append(word) result
['昨天', '大碗', '米饭', '真的', '好吃']
综合
前面我们学习了中文分词、去除停用词、去除标点符号。在实际应用的时候当然还是把这些功能封装为一个函数,即提高效率也提高代码可读性。这里我放出我自己封装的函数供大家参考,可以直接拿去用。
import re import jieba def chinese_word_cut(mytext): jieba.load_userdict('自定义词典.txt') # 这里你可以添加jieba库识别不了的网络新词,避免将一些新词拆开 jieba.initialize() # 初始化jieba # 文本预处理 :去除一些无用的字符只提取出中文出来 new_data = re.findall('[\u4e00-\u9fa5]+', mytext, re.S) new_data = " ".join(new_data) # 文本分词 seg_list_exact = jieba.lcut(new_data) result_list = [] # 读取停用词库 with open('停用词库.txt', encoding='utf-8') as f: # 可根据需要打开停用词库,然后加上不想显示的词语 con = f.readlines() stop_words = set() for i in con: i = i.replace("\n", "") # 去掉读取每一行数据的\n stop_words.add(i) # 去除停用词并且去除单字 for word in seg_list_exact: if word not in stop_words and len(word) > 1: result_list.append(word) return result_list
封装好之后,我们需要用的时候直接调用即可。
text3 = '昨天我吃了一大碗米饭,真的是太好吃了!###@' chinese_word_cut(text3)
['昨天', '大碗', '米饭', '真的', '好吃']
小案例 :将txt文件中的评论内容进行分词后输出为一个新的txt文件
# 加载数据 txt_path = '评论内容.txt' # 你的txt文件的路径,绝对路径和相对路径都可,我这里是相对路径 comment_list = [] with open(txt_path)as f: for i in f.readlines(): i = i.replace('\n','') comment_list.append(i) import re import jieba # 定义分词函数 def chinese_word_cut(mytext): jieba.load_userdict('自定义词典.txt') # 这里你可以添加jieba库识别不了的网络新词,避免将一些新词拆开 jieba.initialize() # 初始化jieba # 文本预处理 :去除一些无用的字符只提取出中文出来 new_data = re.findall('[\u4e00-\u9fa5]+', mytext, re.S) new_data = " ".join(new_data) # 文本分词 seg_list_exact = jieba.lcut(new_data) result_list = [] # 读取停用词库 with open('停用词库.txt', encoding='utf-8') as f: # 可根据需要打开停用词库,然后加上不想显示的词语 con = f.readlines() stop_words = set() for i in con: i = i.replace("\n", "") # 去掉读取每一行数据的\n stop_words.add(i) # 去除停用词并且去除单字 for word in seg_list_exact: if word not in stop_words and len(word) > 1: result_list.append(word) return result_list # 保存分词结果 save_path = '分词后的数据.txt' # 要保存的分词文件的路径 with open(save_path,'a',encoding='utf-8')as f: for comment in comment_list: cutted_comment = ' '.join(chinese_word_cut(comment)) if cutted_comment: f.write(cutted_comment) f.write('\n')
评论内容.txt
分词后的数据.txt
案例实战
中文分词是文本分析必须要经历的环节,关于具体案例,我前面写了很多,大家可以学习参考。