概述
本課程作業(yè)主要借助python工具,實現(xiàn)了N-gram分詞中的Unigram和Bigram分詞器葬荷,并將前向最大切詞FMM和后向最大切詞的結(jié)果作為Baseline纽帖,對比分析N-gram分詞器在詞語切分正確率扒吁、詞義消歧和新詞識別等方面的優(yōu)勢室囊。 github地址
數(shù)據(jù)說明
本實驗使用的語料是人民日報1998年中文標(biāo)注的語料庫,19484條粗蔚。在處理過程中鹏控,按照訓(xùn)練集 : 測試集 = 9 : 1的比例進行隨機劃分肤寝。 數(shù)據(jù)預(yù)處理包括:去詞性当辐、去文本行標(biāo)識(19980101-01-001-001)、詞典統(tǒng)計醒陆、標(biāo)點統(tǒng)計等瀑构。
主要流程
- 文本預(yù)處理,分為:語料隨機切分刨摩、去詞性寺晌、統(tǒng)計詞典等
- 使用前向FMM和后向BMM最大切分,對測試語料進行切分澡刹,統(tǒng)計準(zhǔn)確率呻征、召回率和F1值
- 統(tǒng)計訓(xùn)練語料詞典概率,用Unigram模型對待切分文本采用遞歸的思想罢浇,進行最大概率切分攒岛,統(tǒng)計準(zhǔn)確率顺饮、召回率和F1值
- 統(tǒng)計Bigram詞典概率,用Bigram模型對文本進行切分方案概率計算,選取概率最大切分局蚀,統(tǒng)計準(zhǔn)確率千扶、召回率和F1值
- 對比分析不同切詞方案對文本歧義和未登錄詞的處理效果。
算法描述
1. 文本預(yù)處理
"""語料的隨機切分括饶,默認按照9 : 1的比例切分訓(xùn)練集合測試集"""
def splitCorpus(train=0.9, fileName='199801.txt'):
train_file = open('train.txt', 'wb')
test_file = open('test.txt', 'wb')
with open(fileName, 'rb') as f:
for line in f:
if random() <= train:
train_file.write(line)
else:
test_file.write(line)
train_file.close()
test_file.close()
print('successfully to split corpus by train = %f test = %f' %
(train, 1 - train))
"""統(tǒng)計語料詞典"""
def toWordSet(file_name='train.txt', is_save=False, save_file='wordSet.pkl'):
# 獲取詞典
word_dict = defaultdict(float)
with open(file_name, 'rb') as f:
for line in f:
content = line.decode('gbk').strip().split()
# 去掉第一個詞“19980101-01-001-001/m”
for word in content[1:]:
word_dict[word.split(u'/')[0]] += 1
if is_save:
# 保存wordSet以復(fù)用
joblib.dump(word_dict, save_file)
print("successfully get word dictionary!")
print("the total number of words is:{0}".format(len(word_dict.keys())))
return word_dict
2. FMM和BMM
- 前向最大切詞藤滥,是以可變滑動窗口對文本進行順序取詞时呀,若改詞在詞典中存在磺陡,則進行一次切分;否則彰阴,縮小窗口大小射众,繼續(xù)取詞與詞典庫進行搜索罗洗,知道窗口詞長為1仇让。后向切詞原理相似,只不過是從后面開始進行窗口滑動。
def forwardMaxCut(ustring, word_set, word_max_len=5):
"""
前向最大切詞
:param ustring: 待切詞文本
:param word_set: 詞典
:param word_max_len: 最大詞長
:return: 詞列表
"""
wordList = []
if not ustring:
return wordList
while ustring:
sentence_len = len(ustring)
if sentence_len < word_max_len:
word_max_len = sentence_len
for i in range(word_max_len, 0, -1):
if ustring[:i] in word_set or i == 1:
wordList.append(ustring[:i])
ustring = ustring[i:]
break
else:
i -= 1
return wordList
-
運行結(jié)果:
前向分詞結(jié)果: successfully to split corpus by train = 0.900000 test = 0.100000 the total number of punction is:47 the total number of words is:53198 召回率為:0.9466013860392212 準(zhǔn)確率為:0.9154134377927275 F值為:0.9307462195496794 后向分詞結(jié)果: successfully to split corpus by train = 0.900000 test = 0.100000 the total number of punction is:47 the total number of words is:53767 召回率為:0.950686195146746 準(zhǔn)確率為:0.92130516483316 F值為:0.9357651113664159
由于每次運行,都會對語料進行隨機切分,因此運行結(jié)果中的詞典大小有出入荆永。
- Unigram分詞
- Unigram切詞的計算公式如下:
- 基本思路: 首先統(tǒng)計出訓(xùn)練集詞典中各個詞的頻率氓拼,用來表示公式中的wi;然后凭迹,對待切分文本的進行某種策略的切分,遞歸選擇切分概率最大的子切分序列羹铅,最后回溯得到最大概率切分哥蔚。
- 舉個栗子:
例句S:我是北京大學(xué)的一名研究生
# S的切分可以拆成兩步
P(S) = P(我)*P(是北京大學(xué)的一名研究生)
#同時后面的子句,繼續(xù)可以拆成:
P(S) = P(我)*P(是北京大學(xué)的一名研究生) = P(我)*P(是)*p(北京大學(xué)的一名研究生)
#這里有個問題咕晋,我們是如何知道應(yīng)該拆成“我”和“是”兩個詞质蕉,而不是“我是”一個詞呢
#上面計算最大概率,是遞歸調(diào)用的灾常,假設(shè)我們開始有兩種切分
[我沈撞,是北京大學(xué)的一名研究生]
[我是,北京大學(xué)的一名研究生]
#計算組合概率
P1 = P(我)*P(是北京大學(xué)的一名研究生)
P2 = P(我是)*P(北京大學(xué)的一名研究生)
#我們會發(fā)現(xiàn)
P1 > P2
#對于后面的任何子句雕什,我們都采用無腦切分缠俺,即設(shè)置最大詞長显晶,這里假設(shè)為3,可得到以下切分:
[我壹士,是北京大學(xué)的一名研究生]
[我是磷雇,北京大學(xué)的一名研究生]
[我是北,京大學(xué)的一名研究生]
#然后分別遞歸計算
#為了滿足性能的要求躏救,避免重復(fù)計算唯笙,我們采用將間接計算的子序列的組合概率,都存儲起來
#每次計算新的子序列時盒使,先查看子序列的切分組合中崩掘,是否包含已經(jīng)計算過的子子序列,包含少办,則直接復(fù)用
- 平滑苞慢,對于詞典中搜索不到的詞,需要做一定的平滑處理英妓,常用的平滑方法原理見這里枉疼,本課程實驗支持加1平滑、WItten-Bell平滑方法鞋拟,默認采用的是Wittten-Bell平滑方法骂维。以下為計算最大切分概率程序:
def maxP(self, sentence):
'''
計算最大切分方案
:param sentence: 待切分句子
:return:
'''
# 遍歷所有切分組合中,找出最大概率切分
if len(sentence) <= 1:
return self.DICT.getPValue(self, sentence)
# 判斷切詞方向:backward 或 forward
sentence_split_words = [self.backwardSplitSentence(
sentence), self.forwardSplitSentence(sentence)][self.split_way != 'back']
# 記錄最大概率值
max_p_value = 0
# 儲存最大概率下的切分組合
word_pairs = []
# 組合概率值
word_p = 0
for pair in sentence_split_words:
p1, p2 = 0, 0
if pair[0] in self.value_dict:
p1 = self.value_dict[pair[0]]
else:
p1 = self.maxP(pair[0])
if pair[1] in self.value_dict:
p2 = self.value_dict[pair[1]]
else:
p2 = self.maxP(pair[1])
word_p = p1 * p2
if max_p_value < word_p:
max_p_value = word_p
word_pairs = pair
# 在詞典中查詢當(dāng)前句對應(yīng)的頻率贺纲,不存在時航闺,返回 1/N
sentence_p_value = self.DICT.getPValue(self, sentence)
# 不切分概率最大時,更新各值
if sentence_p_value > max_p_value and self.DICT.inDict(self, sentence):
self.value_dict[sentence] = sentence_p_value
self.seg_dict[sentence] = sentence
return sentence_p_value
# 某種切分組合概率最大時猴誊,更新sentence對應(yīng)概率潦刃,避免后續(xù)切分重復(fù)計算
else:
self.value_dict[sentence] = max_p_value
self.seg_dict[sentence] = word_pairs
return max_p_value
- 運行結(jié)果:
successfully to split corpus by train = 0.900000 test = 0.100000
the total number of words is:53705
the total number of punction is:47
召回率為:0.9614382160763091
準(zhǔn)確率為:0.9319770859102912
F值為:0.9464784466054017
3. Bigram分詞
- Bigram切詞的計算公式如下:
- 基本思路: 首先統(tǒng)計出訓(xùn)練集詞典中各個Bigram的頻率,如[我|是]懈叹、[我|來自]乖杠,用來表示公式中的[wi|wi-1];然后澄成,對待切分文本給出所有的切分方案胧洒,計算切分概率最大的切分序列。
- 舉個栗子:
例句S: 這幾塊地面積還真不小墨状。
#對S進行切分卫漫,獲得所有切分方案
S1 = ['這', '幾', '塊', '地', '面', '積', '還', '真', '不', '小']
S2 = ['這', '幾', '塊', '地', '面', '積', '還', '真', '不小']
S3 = ['這', '幾', '塊', '地', '面積', '還', '真', '不', '小']
S4 = ['這', '幾', '塊', '地', '面積', '還', '真', '不小']
S5 = ['這', '幾', '塊', '地面', '積', '還', '真', '不小']
#利用Bigram公式,計算所有的方案的切分概率肾砂,為了避免出現(xiàn)float下溢出列赎,采用log求和
P(S1) = -64.745
P(S2) = -63.894
P(S3) = -55.041
P(S4) = -54.190
P(S5) = -58.190
P(S4) > P(S3)>P(S5)>P(S2)>P(S1)
#不難發(fā)現(xiàn),上述例句對于機器是一個歧義句镐确,S4和S5兩種切分都可以
#但是根據(jù)語境包吝,S4是正確的
- 運行結(jié)果:
successfully to split corpus by train = 0.900000 test = 0.100000
the total number of words is:53260
The total number of bigram is : 403121.
successfully witten-Bell smoothing! smooth_value:1.3372788850370981e-05
the total number of punction is:47
召回率為:0.962036929819092
準(zhǔn)確率為:0.9401303935308096
F值為:0.950957517059212
4. 結(jié)果分析
- 對比指標(biāo)
指標(biāo) | FMM | BMM | Unigram | Bigram |
---|---|---|---|---|
準(zhǔn)確率 | 91.54% | 92.13% | 93.20% | 94.01% |
召回率 | 94.66% | 95.07% | 96.14% | 96.20% |
F1值 | 93.07% | 93.58% | 94.64% | 95.10% |
- 根據(jù)上表可知:分詞效果最好的是Bigram饼煞,最差的是FMM。 因為FMM只考慮了前向順序詞是否在字典中出現(xiàn)诗越,而Bigram除了考慮詞典中是否包含此詞砖瞧,同時也考慮了鄰接詞對分詞的選擇的影響。 在處理歧義上掺喻,Bigram具有較好的效果芭届,能基本實現(xiàn)消除歧義储矩,但是消除歧義的效果受文本訓(xùn)練大小的影響感耙。 在處理未登錄詞上,這里僅僅是對未登錄詞切分為單個字持隧,因此在未登錄詞的處理上還要進一步的研究討論即硼。