NLP基础-准确分词(使用工具分词)

关于NLP相关包安装配置,可以参考:
NLP工具包安装配置
关于分词的原理可以参考:
自然语言处理NLP-隐马尔科夫)

NLP基础-准确分词(使用工具分词)_第1张图片

1. 加载字典来保证词可以分准

对一些专业的名词来说,使用原有的词库可能无法很好的将词分开,比如在对医疗文本进行分类时,诸如:联合奥沙利铂、氟尿嘧啶单药等专用的药品名词。

jieba中自定义词典的加载

将开始没分准确的词放入字典中,就可以对其正确分词

jieba中的词典,通过json.loads进行加载:

jieba.load_userdict("dict.txt")

例:

import jieba
seg_list = jieba.cut("辅助治疗中的氟尿嘧啶单药或联合奥沙利铂,并未直接纳入TNM分期系统", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list))  

在这里插入图片描述
加载字典后:

import jieba
jieba.load_userdict("dict.txt")
seg_list = jieba.cut("辅助治疗中的氟尿嘧啶单药或联合奥沙利铂,并未直接纳入TNM分期系统", cut_all=False)
print("Default Mode: " + "/ ".join(seg_list))  # 精确模式

在这里插入图片描述

hanlp中自定义词典的加载

hanlp的自定义字典的目录:E:\NLP\hanlp\data\dictionary\custom
由hanlp.properties文件可以看到,其中自定义词典的加载为:resume_nouns.txt文件
NLP基础-准确分词(使用工具分词)_第2张图片
在改动之前,先把CustomDictionary.txt.bin的文件删除,否则加载的都是txt.bin文件
NLP基础-准确分词(使用工具分词)_第3张图片
我们可以看到hanlp的词典和jieba中词典的区别是hanlp的词典中包括词、词性、词频组成,jieba的词典中只是词。

2. 通过正则匹配的方法来将词进行分开

适用于数字等百分号的匹配,无法准确的一一加入字典准确分词的形式,则可以通过正则匹配进行准确分词:

根本原理就是使用正则表达式匹配替换后,再把正则匹配上替换的词替换为原来的词。

cut_data.py:

import jieba
import re
from tokenizer import cut_hanlp

def merge_two_list(a, b):
    c=[]
    len_a, len_b = len(a), len(b)
    minlen = min(len_a, len_b)
    for i in range(minlen):
        c.append(a[i])
        c.append(b[i])

    if len_a > len_b:
        for i in range(minlen, len_a):
            c.append(a[i])
    else:
        for i in range(minlen, len_b):
            c.append(b[i])
    return c
    
if __name__=="__main__":
    # 第一步首先打开需要分词的文件
    fp=open("text.txt","r",encoding="utf8")
    # 第二步创建需要保存分词后结果的文件
    fout=open("result_cut.txt","w",encoding="utf8")
    # 第三步使用正则进行清洗,将一些词进行处理
    # 匹配出非汉字的情况,并且限制在5个字符
    regex1=u'(?:[^\u4e00-\u9fa5()*&……%¥$,,。.@! !]){1,5}期'
    # 这个正则用来匹配百分号的小数{1-3}表示3位,[0-9]表示10个数字
    # 小数点.后打上问号,表示1个或者0个
    regex2=r'(?:[0-9]{1,3}[.]?[0-9]{1,3})%'
    p1=re.compile(regex1)
    p2=re.compile(regex2)
    # 读取每一行
    for line in fp.readlines():
        result1=p1.findall(line)
        # 将正则匹配的词进行替换
        if result1:
            regex_re1=result1
            line=p1.sub("FLAG1",line)
        result2=p2.findall(line)
        if result2:
            line=p2.sub("FLAG2",line)
        # 开始切完词后是迭代器类型,还需要join进行显示
        words=jieba.cut(line)
        words1=cut_hanlp(line)
        result=" ".join(words1)
        # 再把正则匹配上替换的词替换为原来的词
        if "FLAG1" in result:
            # 先通过split方法拆分为列表
            result=result.split("FLAG1")
            # 再通过merge方法将两个列表合并
            result=merge_two_list(result,result1)
            result ="".join(result)
        if "FLAG2" in result:
            result=result.split("FLAG2")
            result=merge_two_list(result,result2)
            result="".join(result)
        fout.write(result)
    fout.close()

tokenizer.py:

import os,gc,re,sys

from jpype import *

startJVM(getDefaultJVMPath(),r"-Djava.class.path=E:\NLP\hanlp\hanlp-1.5.0.jar;E:\NLP\hanlp",
         "-Xms1g",
         "-Xmx1g")

Tokenizer = JClass('com.hankcs.hanlp.tokenizer.StandardTokenizer')

def to_string(sentence,return_generator=False):
    if return_generator:
        return (word_pos_item.toString().split('/') for word_pos_item in Tokenizer.segment(sentence))
    else:
        return " ".join([word_pos_item.toString().split('/')[0] for word_pos_item in Tokenizer.segment(sentence)]   )
    
def seg_sentences(sentence,with_filter=True,return_generator=False):  
    segs=to_string(sentence,return_generator=return_generator)
    if with_filter:
        g = [word_pos_pair[0] for word_pos_pair in segs if len(word_pos_pair)==2 and word_pos_pair[0]!=' ' and word_pos_pair[1] not in drop_pos_set]
    else:
        g = [word_pos_pair[0] for word_pos_pair in segs if len(word_pos_pair)==2 and word_pos_pair[0]!=' ']
    return iter(g) if return_generator else g

def cut_hanlp(raw_sentence,return_list=True):
    if len(raw_sentence.strip())>0:
        return to_string(raw_sentence) if return_list else iter(to_string(raw_sentence))

3. 动态调整字典词频

有时会出现字典已经加载,但是词不一定能分得开的情况,这时就需要通过改变词频率的方法进行动态调整。

jieba中调整词频

jieba.suggest_freq('台中' , tune=True)
import jieba
jieba.load_userdict("dict.txt")
jieba.suggest_freq('台中' , tune=True)
if __name__ = "__main__":
	string = "台中正确应该不会被切开"
	words = jieba.cut(string,HMM=False)
	result = " ".join(words)
	print(result)

但在实际应用中,不可能一句话一句话进行加载,那这样就可以采用先open打开这个字典文件,再对字典文件中的词进行一一遍历,如下:

fp = open("dict.txt", 'r' , encoding = 'utf8')
for line in fp:
	lline = ine.strip()
	jieba.suggest_freq(line, true = True)

为了让运行更高效些,将for循环改为列表生成式:

[jieba.suggest_freq(line.strip(), tune=True) for line in open("dict.txt",'r',encoding='utf8')]

hanlp中调整词频

对于hanlp切词而言,因为hanlp的词典是同时记录单词、词性、词频数的,如果出现了相同词频数的单词,该如何进行选择切分?如:
在这里插入图片描述
这里我们默认最长匹配原则,即优先对len长的进行切分,而Hanlp切词时会按照字典加载的顺序进行切词,单词如果优先加载,就会被切除。故我们需要先对词典中的词按照长度进行排序。
sort_dict_by_len.py:

import os
# 第一步先打开文件
dict_file=open(r"E:\NLP\hanlp\data\dictionary\custom"+os.sep+"resume_nouns.txt",'r',encoding='utf8')
d={}
# 第二步把单词取出,对每个词和每个长度建立一个字典
[d.update({line:len(line.split(" ")[0])}) for line in dict_file]
f=sorted(d.items(), key=lambda x:x[1], reverse=True)
dict_file=open(r"E:\NLP\hanlp\data\dictionary\custom"+os.sep+"resume_nouns1.txt",'w',encoding='utf8')
[dict_file.write(item[0]) for item in f]
dict_file.close()

再进行hanlp切词后结果如下:

from tokenizer import cut_hanlp
if __name__=="__main__":
    string="台中正确应该不会被切开。"
    words=cut_hanlp(string)
    print(words)

在这里插入图片描述

你可能感兴趣的:(NLP(代码实践类))