自然语言处理中最大概率法教材算法实现
参考了https://blog.csdn.net/chase1998/article/details/83247192
把概率的相乘转为负对数的相加。
词典的txt:https://pan.baidu.com/s/1ARx3-fetzOmrw2c8mVAK8w
提取码:hts4
import math
def load_dict():
"""
加载字典
:return:返回字典==>"词:频率"
"""
dic_file = open("WordFrequency.txt", 'r', encoding='utf-8')
freq_dict = {}
for l in dic_file:
freq_dict[l.split(',')[0]] = l.split(',')[2].strip()
#以逗号为分割的字符串的第一部分词的概率,strip()方法移除字符串头尾指定的字符(默认为空格或换行符)
dic_file.close()
#print("freq_dict:",freq_dict)
return freq_dict
def find_word_in_dict(s):
"""
在字典中查找候选词
:param s: 输入句子
:return: 返回字典==>"词:词频|候选左邻词1/候选左邻词2"
"""
print("========================在字典中查找候选词===================================")
freq_dict = load_dict()
result = {}
for index in range(0, len(s)): # 遍历所有字
for wordLen in range(0, len(s) - index): # 遍历该字的所有可能词
seg_word = s[index:index + wordLen + 1]
if seg_word in freq_dict.keys():
# 找到候选词,找其左邻词
left_words = ''
for word_len in range(index, 0, -1): # 在之前的候选词列表里找左邻词(最大词长开始)
for k in result.keys():
if s[index - word_len:index] == k.split('-')[1]:
left_words += str(index - word_len) + '-' + s[index - word_len:index] + '/'
# 返回候选词及其语料库词频和候选左邻词
result[str(index) + '-' + seg_word] = freq_dict[seg_word] + '|' + left_words
print("result:",result)
return result
def cl_probability(words_dict):
"""
计算累加花费并选择最佳左邻词
:param words_dict: "词:词花费|候选左邻词1/候选左邻词2"
:return:返回新字典==>"词:累计花费|最佳左邻词"
"""
print("===================计算累加花费并选择最佳左邻词==============================")
for k, v in words_dict.items():#items() 方法把字典中每对 key 和 value 组成一个元组
t1= v.split('|')[0][:-1]
cost=-math.log(float(t1)*0.01) #取负对数
left_words = v.split('|')[1]
words_dict[k] = v.replace(t1, str(cost)).replace('%','')#用负对数替换概率,并且把百分号去掉
if left_words == '':#无左邻词
continue
else:
left_word = left_words.split("/")
min_left_c = 10000#每个词的最小左邻词花费初始为10000
which_word = ''#选择的词
for num in range(0, len(left_word) - 1):#在每个词的左邻词中寻找最佳左邻词
t=words_dict[left_word[num]].split('|')[0][:-1]
curr_left_word_c = float(t)
if curr_left_word_c < min_left_c: # 比较当前左邻词的累计花费
min_left_c = curr_left_word_c
which_word = left_word[num]
curr_min_c = min_left_c + cost
#多字符替换:用最小累计花费替换原来的词频,用最佳左邻词替换候选左邻词,去掉%
words_dict[k] = v.replace(left_words, which_word).replace( str(t1), str(curr_min_c)).replace('%','')
print("words_dict:",words_dict,"\n")
return words_dict
def seg(sentence):
"""
接收输入,调用函数并输出
:param sentence:
:return: 分词后的句子
"""
words_dict = find_word_in_dict(sentence)
best_words_dict = cl_probability(words_dict)
seg_line = ''
keys = list(best_words_dict.keys())
key = keys[-1]
print("========================分词后的句子===================================")
while key != '':
seg_line = key.split('-')[1] + '/ ' + seg_line
key = best_words_dict[key].split('|')[1]
print("seg_line:",seg_line)
return seg_line
if __name__ == '__main__':
#print("概率最大化分词:")
#input_str = input()结合成分子时
input_str = "结合成分子时"
print(seg(input_str))
结果:
========================在字典中查找候选词===================================
result: {'0-结': '0.0037%|'}
result: {'0-结': '0.0037%|', '0-结合': '0.0353%|'}
result: {'0-结': '0.0037%|', '0-结合': '0.0353%|', '1-合': '0.0049%|0-结/'}
result: {'0-结': '0.0037%|', '0-结合': '0.0353%|', '1-合': '0.0049%|0-结/', '1-合成': '0.0006%|0-结/'}
result: {'0-结': '0.0037%|', '0-结合': '0.0353%|', '1-合': '0.0049%|0-结/', '1-合成': '0.0006%|0-结/', '2-成': '0.0423%|0-结合/1-合/'}
result: {'0-结': '0.0037%|', '0-结合': '0.0353%|', '1-合': '0.0049%|0-结/', '1-合成': '0.0006%|0-结/', '2-成': '0.0423%|0-结合/1-合/', '2-成分': '0.0023%|0-结合/1-合/'}
result: {'0-结': '0.0037%|', '0-结合': '0.0353%|', '1-合': '0.0049%|0-结/', '1-合成': '0.0006%|0-结/', '2-成': '0.0423%|0-结合/1-合/', '2-成分': '0.0023%|0-结合/1-合/', '3-分': '0.0312%|1-合成/2-成/'}
result: {'0-结': '0.0037%|', '0-结合': '0.0353%|', '1-合': '0.0049%|0-结/', '1-合成': '0.0006%|0-结/', '2-成': '0.0423%|0-结合/1-合/', '2-成分': '0.0023%|0-结合/1-合/', '3-分': '0.0312%|1-合成/2-成/', '3-分子': '0.0038%|1-合成/2-成/'}
result: {'0-结': '0.0037%|', '0-结合': '0.0353%|', '1-合': '0.0049%|0-结/', '1-合成': '0.0006%|0-结/', '2-成': '0.0423%|0-结合/1-合/', '2-成分': '0.0023%|0-结合/1-合/', '3-分': '0.0312%|1-合成/2-成/', '3-分子': '0.0038%|1-合成/2-成/', '4-子': '0.0010%|2-成分/3-分/'}
result: {'0-结': '0.0037%|', '0-结合': '0.0353%|', '1-合': '0.0049%|0-结/', '1-合成': '0.0006%|0-结/', '2-成': '0.0423%|0-结合/1-合/', '2-成分': '0.0023%|0-结合/1-合/', '3-分': '0.0312%|1-合成/2-成/', '3-分子': '0.0038%|1-合成/2-成/', '4-子': '0.0010%|2-成分/3-分/', '5-时': '0.1043%|3-分子/4-子/'}
===================计算累加花费并选择最佳左邻词==============================
words_dict: {'0-结': '10.20459264532005|', '0-结合': '7.949042501030977|', '1-合': '20.12828290517365|0-结', '1-合成': '0.0006%|0-结/', '2-成': '0.0423%|0-结合/1-合/', '2-成分': '0.0023%|0-结合/1-合/', '3-分': '0.0312%|1-合成/2-成/', '3-分子': '0.0038%|1-合成/2-成/', '4-子': '0.0010%|2-成分/3-分/', '5-时': '0.1043%|3-分子/4-子/'}
words_dict: {'0-结': '10.20459264532005|', '0-结合': '7.949042501030977|', '1-合': '20.12828290517365|0-结', '1-合成': '22.228343734056217|0-结', '2-成': '0.0423%|0-结合/1-合/', '2-成分': '0.0023%|0-结合/1-合/', '3-分': '0.0312%|1-合成/2-成/', '3-分子': '0.0038%|1-合成/2-成/', '4-子': '0.0010%|2-成分/3-分/', '5-时': '0.1043%|3-分子/4-子/'}
words_dict: {'0-结': '10.20459264532005|', '0-结合': '7.949042501030977|', '1-合': '20.12828290517365|0-结', '1-合成': '22.228343734056217|0-结', '2-成': '15.717180879948966|0-结合', '2-成分': '0.0023%|0-结合/1-合/', '3-分': '0.0312%|1-合成/2-成/', '3-分子': '0.0038%|1-合成/2-成/', '4-子': '0.0010%|2-成分/3-分/', '5-时': '0.1043%|3-分子/4-子/'}
words_dict: {'0-结': '10.20459264532005|', '0-结合': '7.949042501030977|', '1-合': '20.12828290517365|0-结', '1-合成': '22.228343734056217|0-结', '2-成': '15.717180879948966|0-结合', '2-成分': '18.629058843066094|0-结合', '3-分': '0.0312%|1-合成/2-成/', '3-分子': '0.0038%|1-合成/2-成/', '4-子': '0.0010%|2-成分/3-分/', '5-时': '0.1043%|3-分子/4-子/'}
words_dict: {'0-结': '10.20459264532005|', '0-结合': '7.949042501030977|', '1-合': '20.12828290517365|0-结', '1-合成': '22.228343734056217|0-结', '2-成': '15.717180879948966|0-结合', '2-成分': '18.629058843066094|0-结合', '3-分': '23.78968825010375|2-成', '3-分子': '0.0038%|1-合成/2-成/', '4-子': '0.0010%|2-成分/3-分/', '5-时': '0.1043%|3-分子/4-子/'}
words_dict: {'0-结': '10.20459264532005|', '0-结合': '7.949042501030977|', '1-合': '20.12828290517365|0-结', '1-合成': '22.228343734056217|0-结', '2-成': '15.717180879948966|0-结合', '2-成分': '18.629058843066094|0-结合', '3-分': '23.78968825010375|2-成', '3-分子': '25.895105278186847|2-成', '4-子': '0.0010%|2-成分/3-分/', '5-时': '0.1043%|3-分子/4-子/'}
words_dict: {'0-结': '10.20459264532005|', '0-结合': '7.949042501030977|', '1-合': '20.12828290517365|0-结', '1-合成': '22.228343734056217|0-结', '2-成': '15.717180879948966|0-结合', '2-成分': '18.629058843066094|0-结合', '3-分': '23.78968825010375|2-成', '3-分子': '25.895105278186847|2-成', '4-子': '30.14198430803632|2-成分', '5-时': '0.1043%|3-分子/4-子/'}
words_dict: {'0-结': '10.20459264532005|', '0-结合': '7.949042501030977|', '1-合': '20.12828290517365|0-结', '1-合成': '22.228343734056217|0-结', '2-成': '15.717180879948966|0-结合', '2-成分': '18.629058843066094|0-结合', '3-分': '23.78968825010375|2-成', '3-分子': '25.895105278186847|2-成', '4-子': '30.14198430803632|2-成分', '5-时': '32.76075938115034|3-分子'}
========================分词后的句子===================================
seg_line: 时/
seg_line: 分子/ 时/
seg_line: 成/ 分子/ 时/
seg_line: 结合/ 成/ 分子/ 时/
结合/ 成/ 分子/ 时/