【NLP】最大概率法(负对数相加)

自然语言处理中最大概率法教材算法实现

参考了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: 结合/ 成/ 分子/ 时/ 
结合/ 成/ 分子/ 时/ 

 

你可能感兴趣的:(NLP)