首页 > 技术文章 > Bigram-MLE语言模型和模拟输入法的python实现

liweikuan 2021-01-07 09:14 原文

一、题目要求

 二、实验数据集

          https://github.com/liweikuan123/-.git

 

三、python代码

 

function.py(对语料进行处理,生成用于构建二元语言模型和模拟输入法的中间文件)

  1 import re
  2 import jsonlines
  3 
  4 
  5 # 训练语料:metadata.txt
  6 # 生成文档: 1. d.jsonl文件,  2. output.jsonl文件  3. generator_5possible_value.jsonl文件   4. data.txt文件
  7 
  8 
  9 
 10 new_ll=[]  #词汇表:存储单词
 11 dict_file={}  #cut函数中,对每一行数据切分后,产生的中间存储变量, 字典格式:dict{the:[boy,boy, girl,apple, apple,...], boy:[like,like, eat, play,play.....], like:[eatting, playing.....], eatting:[apple,apple.... ].....}
 12 total_list=[] #全部单词(包含重复单词)
 13 d={} #存储每个单词以及它的统计频数, d{read:13, the:10,  a:12, book: 15,......... }
 14 frequency_dict={} #用于封装key_value{}的中间存储变量。
 15 key_value={} #在output.jsonl文件中按行存储的字典
 16 ###############################################################################################################
 17 key_5value={}  #用于封装value{}的中间存储变量。
 18 word_num=5  #每个单词后最可能出现的单词的数目。
 19 value={}   #generator_5possible_value.jsonl文件中按行存储的字典
 20 
 21 
 22 
 23 #count_list()函数:用于统计词汇表中的单词
 24 #new_ll中存储词汇表中的单词
 25 def count_list(list_file):
 26     #用一个列表记录总共多少种单词
 27     global new_ll
 28     for i in list_file:
 29         if i not in new_ll:
 30             new_ll.append(i)
 31 
 32 
 33 
 34 #存储字典d (字典中包含键值对,例如:{read:13, the:10,  a:12, book: 15,......... },生成d.jsonl文件。
 35 def save_dict(line):
 36     with jsonlines.open('i:\d.jsonl', mode='w') as writer:
 37         writer.write(line)
 38 
 39 
 40 
 41 #遍历total_list列表,对列表中的单个单词进行统计数目,将结果存入d[]字典内,例如:d{ read : 20,  the : 30,....}
 42 def count_total_list(total_list):
 43  #用一个字典记录结果 ,遍历列表 , 求count()
 44     global new_ll
 45     for i in new_ll:
 46         d[i]=total_list.count(i) #统计某个单词在训练语料中出现的频数。
 47     save_dict(d)  #存储d字典
 48 
 49 
 50 
 51 #对一行数据进行切分,例如:the boy like eatting apple, 切分为:{the:[boy], boy:[like], like:[eatting], eatting:[apple].......}
 52 #如果dict_file中存在某一个key=result_list[i],将result_list[i+1]添加到dict_file[result_list[i]]的列表中。
 53 #构建dict_file字典,字典最终格式:dict{the:[boy,boy, girl,apple, apple,...], boy:[like,like, eat, play,play.....], like:[eatting, playing.....], eatting:[apple,apple.... ].....}
 54 def cut(result_list):
 55    global dict_file
 56    for i in range(len(result_list)-1):
 57        if result_list[i] not in dict_file:
 58           dict_file[result_list[i]]=[result_list[i+1]]
 59        else:
 60            dict_file[result_list[i]].append(result_list[i+1])
 61 
 62 
 63 #对frequency_count()函数产生的key_value{}进行存储。
 64 #以json格式按行存储字典元素(字典元素中包含键值对,每条json数据格式:key_value{read :{ the:10,  a:12, book: 15 } }  ,即以read开头的,二元语法及频数。
 65 #生成output.jsonl文件
 66 def save_key_value(line):
 67     with jsonlines.open('i:\output.jsonl', mode='a') as writer:
 68         writer.write(line)
 69 
 70 
 71 
 72 #对new_ll(词汇表)中各个单词,挑选其后最可能出现的五个单词,生成generator_5possible_value.jsonl文件, 用于模拟输入法程序。
 73 #value字典的格式形式: value { the:30, a:20, book:15, pen:12, apple:2}
 74 def generator_5possible_value(i,data):
 75     global value,key_5value
 76     data_list = list(data.keys())
 77     length = len(data_list)
 78     if length < word_num:
 79         for count in range(length):
 80             value[data_list[count]]=data[data_list[count]]
 81         key_5value[i]=value
 82     else:
 83         for count in range(word_num):
 84             value[data_list[count]] = data[data_list[count]]
 85         key_5value[i] = value
 86     with jsonlines.open('i:\generator_5possible_value.jsonl', mode='a') as writer:
 87         writer.write(key_5value)    #生成generator_5possible_value.jsonl文件,存储每个单词后最有可能出现的五个单词。
 88     key_5value={}  #用于封装value{}的中间存储变量。
 89     value={}
 90 
 91 
 92 
 93 
 94 #对于new_ll中的每一个单词,例如:read,查看是否在dict_file字典中存在key==i,如果存在,对于dict_file[i]中每个j,构建frequency_dict[name]=key,其中name=str(j),key=dict_file[i].count(j)
 95 #对 frequency_dict中的对象,按照频数排序。
 96 #再对frequency_dict进行封装,即:key_value[i]=data
 97 #最终key_value字典格式为:key_value{read: {the:10, a:12, book:5,....}  } 及二元语法中,read后出现的单词,以及各自的频数。
 98 def frequency_count():
 99     global key_value,frequency_dict,new_ll
100     for i in new_ll: #new_ll中每个单词
101         if i in dict_file: #判断是否在dict_file中存在key==i
102           for j in dict_file[i]:
103             name=str(j)
104             key=dict_file[i].count(j)
105             frequency_dict[name]=key
106 
107           data=dict(sorted(frequency_dict.items(), key=lambda d: d[1], reverse=True))#对 frequency_dict中的对象,按照频数排序。
108           # print(data)
109           key_value[i] =data    #对frequency_dict进行封装
110           generator_5possible_value(i,data)   #对new_ll中各个单词,挑选其后最可能出现的五个单词
111           save_key_value(key_value)   #对分装好的key_value进行存储
112           frequency_dict={}
113           key_value = {}
114           # frequency.append(frequency_dict)
115           # frequency_dict = {}
116 
117 
118 
119 
120 def main():
121  with open('i:\metadata.txt',"r",encoding='utf-8') as f:    #设置文件对象
122    with open('i:\data.txt', 'w') as out:
123      for line in f.readlines():  # 依次读取每行
124          str_ =str(line)
125          # print(str_)
126          str_=str_[::-1]
127          count=str_.find("|")
128          # print(str_)
129          # print(count)
130          str_=str_[0:count]
131          str_=str_[::-1]
132          result_list = re.findall('[a-zA-Z0-9]+', str_)
133 
134          result_list.insert(0,"BOS")
135          result_list.append("EOS")
136                            #以上两行代码,对metadata.txt中第三列的每个句子处理为格式:result_list[ 'BOS','the','boy','is','a','very','great','child','EOS']
137          for i in result_list:
138              total_list.append(i)#统计所有单词,包括重复单词。
139          # print(result_list)
140          count_list(result_list)
141          cut(result_list)
142          out.writelines(result_list)  #生成data.txt文件,文件中储存处理后的句子,每个句子处理为格式:result_list[ 'BOS','the','boy','is','a','very','great','child','EOS']。
143          out.writelines("\n")
144               # 以上部分代码,负责对metadata.txt中第三列进行处理,total_list中存储切分后的所有单词(包括重复单词).
145      count_total_list(total_list) #遍历total_list列表,对列表中的单个单词进行统计数目,将结果存入d[]字典内,例如:d{ read : 20,  the : 30,....}
146      print("字典"+str(d))
147      print(len(d))
148      frequency_count()  #生成key_value字典, 最终key_value字典格式为:key_value{read: {the:10, a:12, book:5,....}  } 及二元语法中,read后出现的单词,以及各自的频数。
149      print("字符" + str(new_ll))
150      print(len(new_ll))
151      print("键-值" + str(dict_file))
152      print(len(dict_file))
153      print(len(total_list))
154      out.close()
155      f.close()
156 
157 
158 
159 
160 if __name__ == "__main__":
161       main()

 

 

 LanguageModel.py(Bigram-MLE语言模型和模拟输入法程序)

 

  1 import jsonlines
  2 
  3 d={}   #用于接收d.jsonl文件中的d{}字典
  4 copy_data=[]  #用于接收generator_5possible_value.jsonl文件中的value{}字典
  5 sentence={}  #用于存储输入句子的最终处理结果的字典
  6 probability_key_value={}  #用于存储句子[the first time]中二元语法的概率,例如:格式 {BOS-the:0.028100, the-first:0.009096, first-time:0.034375, time-EOS:0.110638}
  7 
  8 
  9 #init_data()函数
 10 #进行数据初始化,导入d.jsonl文件和generator_5possible_value.jsonl文件。
 11 def init_data():
 12     global d,copy_data
 13     with jsonlines.open('i:\d.jsonl', "r") as dict_reader:
 14         for line in dict_reader: #d{}字典在d.json文件中,实际上存储在一个json文件中,此循环只执行一次。
 15             d = line
 16     with jsonlines.open('i:\generator_5possible_value.jsonl',"r") as reader:
 17         for obj in reader: #在generator_5possible_value.jsonl文件中,存在多个value{}字典,value{}字典的个数等于new_ll(词汇表)中单词的数目,循环执行多次。
 18             copy_data.append(obj)
 19 
 20 
 21 
 22 #find()函数的功能
 23 #对于一个二元语法模型<read a>,计算p(a|read)=c(read a)/c(read),返回计算结果
 24 def find(key,value):
 25     global copy_data
 26     for i in copy_data:
 27         if key in i:
 28             if value in i[key]:
 29                 return i[key][value]
 30             else:
 31                 return 0
 32 
 33 
 34 
 35 #Bigram-MLE模型算法
 36 def algorithm_Bigram(data):
 37     global sentence,d,probability_key_value
 38     probability=1 #存储句子概率的中间变量。
 39 
 40     output=data.split()
 41     output.insert(0,"BOS")
 42     output.append("EOS")
 43             #上面三行代码,对输入的句子进行处理。 例如:句子[ i am a student ]处理结果为['BOS','i','am','a','student','EOS']
 44     for i in range(len(output)-1):
 45         sentence[output[i]]=output[i+1]  #处理后格式:sentence{ BOS:i , i:am, am:a, a:student, student:EOS}
 46     for j in range(len(output)-1):
 47         try:
 48             value= find(output[j],output[j+1])/d[output[j]]
 49         except ZeroDivisionError:
 50                 value=-1
 51                 print("不存在"+str(output[j]))
 52         probability_key_value[str(output[j])+'-'+str(output[j+1])]=value
 53         print(probability_key_value)
 54         probability*=value
 55     # print(sentence)
 56     # print(output)
 57     return probability
 58 
 59 
 60 
 61 
 62 
 63 #模拟输入法:给定一个单词推荐接下来最有可能输入的5个单词。
 64 def algorithm_IME(data):
 65     global  copy_data
 66     for dict_data in copy_data:
 67         if data in dict_data:
 68             print(dict_data[data])
 69         # print(i)
 70         # data2 = json.dumps(history, sort_keys=True, indent=4, separators=(',', ': '))
 71         # print(data2)
 72 
 73 
 74 
 75 
 76 
 77 def main():
 78     init_data()
 79     # print(copy_data)
 80     # print(len(copy_data))
 81     select = input("Bigram-MLE计算语句概率未使用平滑技术(输入1)    模拟输入法(输入2)请输入选择:  ")
 82     if select=='1':
 83         print("Bigram-MLE计算语句概率未使用平滑技术")
 84         data = input("请输入: ")
 85         probability=algorithm_Bigram(data)
 86         print(probability)
 87         print("结束")
 88     if select=='2':
 89         print("模拟输入法")
 90         while True:
 91             data = input ("请输入: ")
 92             if data=='#':
 93                 break
 94             print("你输入的内容是: ", data)
 95             algorithm_IME(data)
 96         print("结束")
 97 
 98 
 99 
100 if __name__ == "__main__":
101       main()

 

 

四、可能的改进

语言模型概率:
1.概率科学计数
为了防止概率计算的时候因为越算越小导致计算机无法比较,所有的概率都进行了自然对数运算
2.引入平滑技术
3.语言模型的性能评估(准备测试集,计算交叉熵和困惑度)

推荐阅读