问题:写一个Python程序,使用给定的语料库(metadata.txt的第三列文本),构建二元语法模型,用MLE(最大似然估计)去估计bigram的概率,可以不使用数据平滑技术。
需要使用控制台输入法演示该语言模型:当输入一个单词后,程序自动推荐接下来最可能输入的5个单词,如果用户根据推荐或自主输入下一个单词,程序以同样的方式推荐接下来最可能输入的5个单词,以此使用户循环输入。
原始数据metadata.txt见此链接。
程序1,extract_data.py:将原始数据metadata.txt的第三列摘出来放到一个新文本文件newdata.txt中。本程序无输出结果,但是会生成一个文件newdata.txt。
# 本程序功能:将原始数据的第三列摘出来放到一个新文本文件中
import os
os.chdir(r'D:\\') # 假设这是原始语料metadata.txt所在的根目录
with open('metadata.txt', 'r', encoding='utf8') as file_object:
a = file_object.readlines() # 将文本的每一行作为一个字符串存在列表a里
s = ''
for i in a:
i = i.split('|') # 每一行按'|'分开
i = i[-1] # 取最后一项,也就是文本内容的第三列
i = i.replace('\n', ' ') # 把换行替换为空格
s += i
with open('newdata.txt', 'w', encoding='utf8') as file_object: # 新内容放在newdata.txt中
file_object.write(s)
程序2,generate_base.py:根据语料库newdata.txt生成一个库文件words_base.json。本程序在运行结束后会输出成功提示,以及程序运行时间(一般需要10分钟左右),还会输出出现次数最多的五个词,另外生成一个文件words_base.json。
# 本程序功能:根据语料库newdata.txt生成一个库文件words_base.json
import os, re, json, time
start = time.time() # 程序开始运行
os.chdir(r'D:\\')
all_words = [] # 用来存放所有单词
with open('newdata.txt', 'r', encoding='utf8') as file_object:
contents = file_object.read() # 将文本中的全部字符作为一个字符串
all_words = re.findall(r'\b[a-zA-Z]+\b', contents) # 所有的单词,每个单词可能出现不止一次,有重复的单词
all = list(sorted(set(all_words))) # 去除多余的单词
l = len(all_words) # 语料库中所有单词的个数,包括重复的单词
class Words: # 构建一个词类
def __init__(self, word):
self.word = word
self.word_times = 0 # 本单词在语料库中出现的次数,开始假设为0
self.word_lists = [] # 所有的本单词后面紧跟着的第一个单词
self.word_dicts = [] # 所有的本单词后面紧跟着的第一个单词中,每个单词对应一个由自身和概率两个字段构成的一个字典,word_dicts为一个由字典组成的列表
words_base = [] # 库
for word in all:
w = Words(word)
tmps = []
for i in range(l):
if all_words[i] == w.word:
w.word_times += 1
if i != l - 1: # 将与当前单词相同的单词的后一个单词存入列表;如果与当前单词相同的是所有单词的最后一个的话,则后面没有单词了,需要跳过
tmps.append(all_words[i + 1])
w.word_lists = list(sorted(set(tmps))) # 去除重复项
for t in w.word_lists:
times = 0
for tmp in tmps:
if t == tmp:
times += 1
w.word_dicts.append({'word':t, 'prob': '%.3f' % (times/w.word_times)}) # 两个字段单词及对应的概率
w.word_dicts = sorted(w.word_dicts, key=lambda x: x['prob'], reverse=True) # 根据概率从大到小,将【字典列表】排序
words_base.append([w.word, w.word_times, w.word_dicts])
words_base = sorted(words_base, key=lambda x: x[1], reverse=True) # 按每个单词在语料库出现的次数由大到小排列
with open('words_base.json', 'w') as file_object: # 把内容保存到json文件中,方便后续使用,否则每次测试时都需要重新运行程序
json.dump(words_base, file_object)
end = time.time() # 程序结束运行
print('Successful!')
print('This program has run for about ' + str(int(end-start)) + 's')
ss = ''
for tt in words_base[:5]:
ss += tt[0] + ' '
print('出现次数最多的五个词是:' + ss)
输出为:
Successful!
This program has run for about 673s
出现次数最多的五个词是:the of and to in
程序3,test.py:本程序主要用来测试:根据提示操作就行,程序会根据用户输入的内容调整输出【最可能输入的5个单词】,并输出目前用户输入的历史,之后不断要求用户输入。若用户输入q,程序停止。
如果输入的词在语料库中,则输出前五个概率最大的单词;如果不够五个,就用总共出现次数最多的几个词去补,缺几个补几个。如果输入的词不在语料库中,则直接输出出现次数最多的五个词。
import os, json
os.chdir(r'D:\\')
with open('words_base.json', 'r') as file_object:
words_base = json.load(file_object) # 将库文件内容加载到变量中
# 下面是出现次数最多的五个词
s = ['the', 'of', 'and', 'to', 'in']
input_words = '' # 输入的单词历史
while True:
ss = '' # 用于输出待选词
input_word = input('请输入一个单词,退出请按"q"\n:')
if input_word == 'q':
break
flag = 0 # 检验输入的单词是否存在于语料库中,开始假设未出现在语料库
for w in words_base:
if w[0] == input_word:
flag = 1 # 出现在了语料库就赋值为1
times = 0
for i in range(len(w[2][:5])): # 只输出前五个
ss += str(i+1) + ' ' + w[2][i]['word'] + ' '
times += 1
if times < 5: # 不够五个就按出现次数最大的单词去补
for i in range(5-times):
ss += str(times+i+1) + ' ' + s[i] + ' '
if flag == 0: # 如果输入的单词不存在于语料库中,则输出出现次数最多的五个词
for i in range(5):
ss += str(i+1) + ' ' + s[i] + ' '
print(ss)
input_words += input_word + ' '
print('您目前已经输入的句子为:', input_words, '\n')
输出:这只是一个我自己输入的序列,你可以自由发挥。
请输入一个单词,退出请按"q"
:You
1 have 2 and 3 can 4 who 5 will
您目前已经输入的句子为: You
请输入一个单词,退出请按"q"
:will
1 be 2 not 3 also 4 have 5 come
您目前已经输入的句子为: You will
请输入一个单词,退出请按"q"
:not
1 be 2 only 3 to 4 have 5 the
您目前已经输入的句子为: You will not
请输入一个单词,退出请按"q"
:have
1 been 2 a 3 to 4 seen 5 the
您目前已经输入的句子为: You will not have
请输入一个单词,退出请按"q"
:a
1 man 2 few 3 great 4 good 5 number
您目前已经输入的句子为: You will not have a
请输入一个单词,退出请按"q"
:good
1 deal 2 a 3 for 4 intentions 5 shot
您目前已经输入的句子为: You will not have a good
请输入一个单词,退出请按"q"
:deal
1 of 2 with 3 and 4 more 5 He
您目前已经输入的句子为: You will not have a good deal
请输入一个单词,退出请按"q"
:q
评价
我没有使用数据平滑技术,这三个程序中有好几个变量的数据类型都是复合类型,较为复杂,且在程序2中定义的Words类的用处也只是为了方便管理变量,并没有实现类方法,后期改进时,可以将程序中的几个程序片段放在类方法中,这样主程序就更易懂了。
END