关于NLP相关包安装配置,可以参考:
NLP工具包安装配置
关于分词的原理可以参考:
自然语言处理NLP-隐马尔科夫)
对一些专业的名词来说,使用原有的词库可能无法很好的将词分开,比如在对医疗文本进行分类时,诸如:联合奥沙利铂、氟尿嘧啶单药等专用的药品名词。
将开始没分准确的词放入字典中,就可以对其正确分词
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的自定义字典的目录:E:\NLP\hanlp\data\dictionary\custom
由hanlp.properties文件可以看到,其中自定义词典的加载为:resume_nouns.txt文件
在改动之前,先把CustomDictionary.txt.bin的文件删除,否则加载的都是txt.bin文件
我们可以看到hanlp的词典和jieba中词典的区别是hanlp的词典中包括词、词性、词频组成,jieba的词典中只是词。
适用于数字等百分号的匹配,无法准确的一一加入字典准确分词的形式,则可以通过正则匹配进行准确分词:
根本原理就是使用正则表达式匹配替换后,再把正则匹配上替换的词替换为原来的词。
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))
有时会出现字典已经加载,但是词不一定能分得开的情况,这时就需要通过改变词频率的方法进行动态调整。
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的词典是同时记录单词、词性、词频数的,如果出现了相同词频数的单词,该如何进行选择切分?如:
这里我们默认最长匹配原则,即优先对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)