關於NLP相關包安裝配置,可以參考:
NLP工具包安裝配置
關於分詞的原理可以參考:
自然語言處理NLP-隱馬爾科夫)
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文件
在改動之前,先把CustomDictionary.txt.bin的文件刪除,否則加載的都是txt.bin文件
我們可以看到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)