语言模型2:二元文法求句子概率代码

在上一篇博客里,简单的介绍了语言模型,其中举了一个例子,这里就用代码来实现实现目标句子出现概率的求值。
语言模型2:二元文法求句子概率代码_第1张图片

回顾

如果想再一次回顾语言模型的理论知识,可以直接点击语言模型(N-Gram)

语料库:

研究生物很有意思。
他是研究应用。
踏实研究生物的。
他实验救生物的。
他大学时代是研究生物的。
生物专业是他的首选目标。

  • 目标实现:使用二元模型以分词模式”<BOS> 他 是 研究 生物 的 <EOS>”计算出现句子“他是研究生物的”的概率。

P(s) = P(他|<BOS>)P(是|他)P(研究|是)P(生物|研究)P(的|生物)P(<EOS>|的)
= 3/6 * 1/4 * 2/3 * 3/4 * 3/5 * 3/4

代码展示

1、导入包

import jieba
import re
from zhon.hanzi import punctuation
from _overlapped import NULL

2、将句子变为"BOSxxxxxEOS"这种形式

def Modify(s):
    #将结尾标点符号截掉
    if s[-1] in (r"[%s]+" %punctuation):
        s = s[:-1]  #截取字符串从开头到倒数一个字符的子串
    
    #添加起始符BOS和终止符EOS   
    s_modify1 = re.sub(r"[%s]+" %punctuation, "EOSBOS", s)   ## r'\w+'为正则表达式,匹配多个英文单词或者数字  
    s_modify2="BOS"+s_modify1+"EOS"
    return s_modify2

输出:
‘BOS研究生物很有意思EOSBOS他是研究实物应用EOSBOS踏实研究生物的EOSBOS他实验救生物的EOSBOS他大学时代是研究生物的EOSBOS生物专业是他的首选目标吗EOS’

3、分词并统计词频

在下面函数里出现的NULL表示空,它不是一个值,这点不同于None。在python这类脚本语言中变量和值是绑定关系,但如果设置一个变量是NULL,实际就是说它没有与任何值绑定,也就是说该变量实际上并未被定义;

def Partition_Statistics(s, lists, dicts = NULL):
    jieba.suggest_freq(("BOS", "EOS"), True)
    s = jieba.cut(s, HMM = False)  #精确模式,自动计算的词频在使用 HMM 新词发现功能时可能无效,所以设为False
    format_s = ",".join(s)
    #将词按","分割后依次填入数组
    lists = format_s.split(",")
    #统计词频
    if dicts != NULL:
        for word in lists:
            if word not in dicts:
                dicts[word] = 1
            else:
                dicts[word] += 1               
    return lists

输出:
[‘BOS’, ‘研究’, ‘生物’, ‘很’, ‘有意思’, ‘EOS’, ‘BOS’, ‘他’, ‘是’, ‘研究’, ‘实物’, ‘应用’, ‘EOS’, ‘BOS’, ‘踏实’, ‘研究’, ‘生物’, ‘的’, ‘EOS’, ‘BOS’, ‘他’, ‘实验’, ‘救’, ‘生物’, ‘的’, ‘EOS’, ‘BOS’, ‘他’, ‘大学’, ‘时代’, ‘是’, ‘研究’, ‘生物’, ‘的’, ‘EOS’, ‘BOS’, ‘生物’, ‘专业’, ‘是’, ‘他’, ‘的’, ‘首选’, ‘目标’, ‘吗’, ‘EOS’]

4、二元语法,比较两个数列,求一个词在历史条件下出现的频数

def CompareList(ori_list,test_list):
    #申请空间
    count_list=[0]*(len(test_list)-1)
    #遍历测试的字符串
    for i in range(0, len(test_list)-1):
        #遍历语料字符串,因为是二元语法,不用比较语料字符串的最后一个字符
        for j in range(0,len(ori_list)-2):                
            #如果测试的第一个词和语料的第一个词相等则比较第二个词
            if test_list[i]==ori_list[j]:
                if test_list[i+1]==ori_list[j+1]:
                    count_list[i]+=1
    return count_list

输出:[3, 1, 2, 3, 3, 3]

5、计算概率

def Probability(test_list,count_list,ori_dict):
    flag=0
    #概率值为p
    p=1
    for i in range(6): 
        p *=(float(count_list[flag])/float(ori_dict[test_list[i]]))
        flag += 1
    return p

6、主函数:组合函数求目标句子出现的概率

if __name__ == "__main__":
​
    #语料句子
    s = "研究生物很有意思!他是研究实物应用。踏实研究生物的。他实验救生物的。他大学时代是研究生物的。生物专业是他的首选目标吗?"
​
    ori_list=[]
    ori_dict={}
​
    #测试句子
    s_test="他是研究生物的"
    test_list=[]
    count_list=[]
​
​
    #分词并将结果存入一个list,词频统计结果存入字典
    s_ori = Modify(s)
    ori_list = Partition_Statistics(s_ori,ori_list,ori_dict)
​
    s_test = Modify(s_test)
    test_list = Partition_Statistics(s_test,test_list)
​
    count_list = CompareList(ori_list, test_list)
    p = Probability(test_list,count_list,ori_dict)
    print(p)

输出:0.028124999999999997

​这样就实现了以分词模式”<BOS> 他 是 研究 生物 的 <EOS>”计算出现句子“他是研究生物的”的概率,为0.028124999999999997。当然,这里只是简单处理了这个问题细节性问题和普遍适用性并没有考虑到,所以这里用到的方法对于可能并不适用于其他的问题,但具有一定的参考性,需要进行一些修改。

你可能感兴趣的:(自然语言处理)