week1
语言模型与语法树
基于规则的语言模型
import random
def adj():
###随机选取一个
return random.choice('蓝色的 | 好看的 | 小小的'.split('|')).split()[0]
def adj_star():
####返回一个adj() 或多个 或者返回''
return random.choice([lambda : '', lambda : adj() + adj_star()])()
adj_star()
----
'蓝色的好看的小小的'
lan根据语法描述生成语法规则
adj_grammar = """
Adj* => null | Adj Adj*
Adj => 蓝色的 | 好看的 | 小小的
"""
# 根据语法描述 grammar_str 生成规则 grammar
def create_grammar(grammar_str, split='=>', line_split='\n'):
grammar = {}
###split() 方法将字符串按行拆分成列表,并返回一个包含每个子列表的元组
###循环遍历 grammar_str 中的每一行,并将其作为元组传递给 split() 方法。
for line in grammar_str.split(line_split):
if not line.strip():
continue ###跳过空行
###将当前行拆分成两个子列表:一个包含表达式,另一个包含语句。
exp, stmt = line.split(split)
grammar[exp.strip()] = [s.split() for s in stmt.split('|')]
return grammar
# # 根据语法描述 adj_grammar 生成语法规则 grammar
grammar = create_grammar(adj_grammar)
print(grammar)
print(grammar['Adj*'])
print(grammar['Adj'])
----
{'Adj*': [['null'], ['Adj', 'Adj*']], 'Adj': [['蓝色的'], ['好看的'], ['小小的']]}
[['null'], ['Adj', 'Adj*']]
[['蓝色的'], ['好看的'], ['小小的']]
根据语法规则生成句子
# 语法描述
###建立语法规则
simple_grammar = """
sentence => noun_phrase verb_phrase
noun_phrase => Article Adj* noun
Adj* => null | Adj Adj*
verb_phrase => verb noun_phrase
Article => 一个 | 这个
noun => 女人 | 篮球 | 桌子 | 小猫
verb => 看着 | 坐在 | 听着 | 看见
Adj => 蓝色的 | 好看的 | 小小的
"""
# 根据语法规则生成句子
choice = random.choice
###gram:调用上面生成规则的函数,target:调用上面的语法
def generate(gram, target):
if target not in gram:
return target # means target is a terminal expression #1
expaned = [generate(gram, t) for t in choice(gram[target])] #2
return ''.join([e if e != '/n' else '\n' for e in expaned if e != 'null']) #3
# 根据语法描述生成语法规则
###调用上面create_grammar函数根据simple_grammar生成语法规则
example_grammar = create_grammar(simple_grammar)
# 查看生成的语法规则
example_grammar
# 根据语法规则生成句子
generate(gram=example_grammar, target='sentence')
-----
'这个桌子看着一个蓝色的蓝色的小小的小猫'
####noun_phrase--Article Adj* noun-->这个 Adj* noun -- 这个 null 桌子
####先生成语法规则 再根据语法规则生成句子
####列二
#一个”人类“的语言可以定义为:
human = """
human => 自己 寻找 活动
自己 => 我 | 俺 | 我们
寻找 => 找找 | 想找点
活动 => 乐子 | 玩的
"""
#一个“接待员”的语言可以定义为
host = """
host = 寒暄 报数 询问 业务相关 结尾
报数 = 我是 数字 号 ,
数字 = 单个数字 | 数字 单个数字
单个数字 = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
寒暄 = 称谓 打招呼 | 打招呼
称谓 = 人称 ,
人称 = 先生 | 女士 | 小朋友
打招呼 = 你好 | 您好
询问 = 请问你要 | 您需要
业务相关 = 玩玩 具体业务
玩玩 = null
具体业务 = 喝酒 | 打牌 | 打猎 | 赌博
结尾 = 吗?
"""
###调用了上面创建的generate和create_grammar函数
for i in range(20):
print(generate(gram=create_grammar(human, split='=>'), target='human'))
print(generate(gram=create_grammar(host, split='='), target='host'))
-----
我们找找玩的 ###human 自己--我们 寻找--找找 活动--玩的
女士,您好我是5号,请问你要喝酒吗? ##host 寒暄--称谓 打招呼--人称,打招呼--女士,您好
我想找点乐子
女士,你好我是39号,您需要赌博吗?
俺找找玩的
你好我是56号,您需要打牌吗?
我们找找玩的
先生,你好我是24号,您需要打牌吗?
###列三
# 例子3:
simpel_programming = '''
programming => if_stmt | assign | while_loop
while_loop => while ( cond ) { change_line stmt change_line }
if_stmt => if ( cond ) { change_line stmt change_line } | if ( cond ) { change_line stmt change_line } else { change_line stmt change_line }
change_line => /N
cond => var op var
op => | == | < | >= | <=
stmt => assign | if_stmt
assign => var = var
var => var _ num | words
words => words _ word | word
word => name | info | student | lib | database
nums => nums num | num
num => 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0
'''
# 根据语法规则生成一段代码
print(generate(gram=create_grammar(simpel_programming, split='=>'), target='programming'))
-----
if(info_student<=lib_5_3){/Ninfo_database_info_info_name_7=name_1_0_7_3/N}else{/Nlib=lib/N}
# 例子4:格式化输出
def pretty_print(line):
# utility tool function
lines = line.split('/N')
code_lines = []
for i, sen in enumerate(lines):
if i < len(lines) / 2:
#print()
code_lines.append(i * " " + sen)
else:
code_lines.append((len(lines) - i) * " " + sen)
return code_lines
generated_programming = []
# 根据语法描述生成20段代码
for i in range(20):
generated_programming += pretty_print(generate(gram=create_grammar(simpel_programming, split='=>'), target='programming'))
# 打印20段代码
for line in generated_programming:
print(line)
-----
while(name>=database_student_1){
if(student_lib_database_namelib_student){
if(student_4<lib_1_1){
if(name_name_student>=lib_lib_info_2){
if(lib_name_0_7<database_3_3){
info_student=database_7
}
}
总结:
从上面代码可以看出大致过程是 添加语法 根据语法产生语法规则 然后再使用语法规则生成句子
Language Model
import random
import jieba
import pandas as pd
import re
from collections import Counter
from functools import reduce
from operator import add, mul
import numpy as np
import matplotlib.pyplot as plt
# %matplotlib inline
数据预处理
# 读取文件
import pandas as pd
filename = 'C:\\Users\dazhi\Desktop\sqlResult_1558435.csv'
content = pd.read_csv(filename, encoding='gb18030')
content.head(10) ##显示10行
# 提取 content 那列
articles = content['content'].tolist()
print(len(articles))
---
89611
词-- 中文需要切词--我想要玩游戏 -- 我 想要 玩 游戏
使用 jieba
# 正则查找所有字词
import re
def token(string):
# we will learn the regular expression next course.
return re.findall('\w+', string)
# 将第110条语句进行分词并计数
from collections import Counter
import jieba
with_jieba_cut = Counter(jieba.cut(articles[110]))
----
Building prefix dict from the default dictionary ...
Dumping model to file cache C:\Users\dazhi\AppData\Local\Temp\jieba.cache
Loading model cost 0.690 seconds.
Prefix dict has been built successfully.
# 词频最高的10个词
with_jieba_cut.most_common()[:10]
---
[(',', 88),
('的', 73),
('。', 39),
('\r\n', 27),
('了', 20),
('们', 18),
('工作队', 16),
('村民', 15),
('收割', 14),
('、', 12)]
# 查找第110条记录的所有字词,无空格连接
''.join(token(articles[110]))
---
'在外国名著麦田....核实相关内容'
# 查找每条记录的所有字词,无空格连接
articles_clean = [''.join(token(str(a)))for a in articles]
print(len(articles_clean))
---
89611
# 保存到文件
with open('E://nlp/article_9k.txt', 'w') as f:
for a in articles_clean:
f.write(a + '\n')
总结
大致过程:读取文件 进行切词 将切词后的文件保存到指定文件
分词
# 定义分词函数
def cut(string):
return list(jieba.cut(string))
# 将保存到文件中的前10000行字词进行分词
TOKEN = []
for i, line in enumerate((open('E://nlp/article_9k.txt'))):
if i % 100 == 0:
print(i) ##以去100为单位输出
# replace 10000 with a big number when you do your homework.
if i > 10000:
break
TOKEN += cut(line)
----
0
100
200
300
400
.
.
9700
9800
9900
10000
# reduce 加操作
reduce(add, [1, 2, 3, 4, 5, 8])
----
23
# 列表加操作
[1, 2, 3] + [3, 43, 5]
---
[1, 2, 3, 3, 43, 5]
# 对分词进行计数
words_count = Counter(TOKEN)
# 词频最高的前100个词
words_count.most_common(100)
----
[('的', 184244),
('在', 47370),
('了', 36722),
.
.
('认为', 3295),
('20', 3282),
('称', 3271)]
# 高频词绘图
import matplotlib.pyplot as plt
# y坐标:前100个高频词的词频
frequiences = [f for w, f in words_count.most_common(100)]
# x坐标:100个词
x = [i for i in range(100)]
# 绘图
plt.plot(x, frequiences)
# 半指数绘图
import numpy as np
plt.plot(x, np.log(frequiences))
总结
对前面保存的文件前10000行字词进行分词
计算概率
# 计算每个词出现的概率
def prob_1(word):
return words_count[word] / len(TOKEN)
prob_1('我们')
----
0.001554473157589251
条件概率:p(w1|w2) = count(w1,w2)/count(w1)
# 转为?(已经是)字符串
TOKEN = [str(t) for t in TOKEN]
TOKEN[:10]
---
['此外', '自', '本周', '6', '月', '12', '日起', '除', '小米', '手机']
# 连接相邻的两个词
TOKEN_2_GRAM = [''.join(TOKEN[i:i+2]) for i in range(len(TOKEN[:-2]))]
TOKEN_2_GRAM[:10]
---
['此外自', '自本周', '本周6', '6月', '月12', '12日起', '日起除', '除小米', '小米手机', '手机6']
# 相邻连词计数
words_count_2 = Counter(TOKEN_2_GRAM)
#print(words_count_2) ###打印会超过IOPub数据速率。
# 计算条件概率
def prob_2(word1, word2): # p(w1,w2) = count(w1,2)/count(w1)
if word1 + word2 in words_count_2:
return words_count_2[word1+word2] / words_count[word1]
else: # 不存在的概率设为非零值
return 1 / len(TOKEN_2_GRAM)
# (w1 w2), (w3,w4) (w4,w5) 2-gram
# (w1,w3) 1/3
print(prob_2('我们', '在'))
print(prob_2('在', '吃饭'))
print(prob_2('去', '吃饭'))
---
0.030128874956461164
2.1110407430863417e-05
2.707199580708929e-07
语言模型
# 基于语言模型,计算一条语句出现的概率
def get_probablity(sentence):
words = cut(sentence)
sentence_pro = 1
###word:当前词 next_:下一个词
for i, word in enumerate(words[:-1]):
next_ = words[i+1]
probability = prob_2(word, next_) # p(w1|w2)
sentence_pro *= probability # p(s) = p(w_1)p(w2|w1)*p(w3|w2)..p(wn|wn-1)
return sentence_pro
print(get_probablity('小明今天抽奖抽到一台苹果手机'))
print(get_probablity('小明今天抽奖抽到一架波音飞机'))
print(get_probablity('洋葱奶昔来一杯'))
print(get_probablity('明天会更好'))
print(get_probablity('明天'))
---
6.743762360853308e-35
7.989690983840629e-36
1.9840875058382383e-20
2.0700271361739125e-05
1
# 根据语法描述生成10个句子,计算出现的概率
for sen in [generate(gram=example_grammar, target='sentence') for i in range(10)]:
print('sentence: {} with Prb: {}'.format(sen, get_probablity(sen)))
---
sentence: 一个女人看见这个蓝色的小小的篮球 with Prb: 4.571169230988429e-32
sentence: 一个小小的篮球看着一个桌子 with Prb: 2.595841515127748e-27
sentence: 这个好看的女人坐在这个蓝色的女人 with Prb: 2.27298831182375e-36
.
.
sentence: 这个小小的小猫听着这个小猫 with Prb: 4.475741062148663e-31
sentence: 一个小小的小猫坐在一个小猫 with Prb: 4.758996630739902e-30
# 比较两个句子出现的概率大小
need_compared = [
"今天晚上请你吃大餐,我们一起吃日料 明天晚上请你吃大餐,我们一起吃苹果",
"真事一只好看的小猫 真是一只好看的小猫",
"今晚我去吃火锅 今晚火锅去吃我",
"洋葱奶昔来一杯 养乐多绿来一杯"
]
for s in need_compared:
s1, s2 = s.split()
p1, p2 = get_probablity(s1), get_probablity(s2)
better = s1 if p1 > p2 else s2
print('{} is more possible'.format(better))
print('-'*4 + ' {} with probility {}'.format(s1, p1))
print('-'*4 + ' {} with probility {}'.format(s2, p2))
---
明天晚上请你吃大餐,我们一起吃苹果 is more possible
---- 今天晚上请你吃大餐,我们一起吃日料 with probility 6.684624207742742e-46
---- 明天晚上请你吃大餐,我们一起吃苹果 with probility 7.542849854956504e-46
真是一只好看的小猫 is more possible
---- 真事一只好看的小猫 with probility 2.1153007661637964e-26
---- 真是一只好看的小猫 with probility 7.813612196297205e-20
今晚我去吃火锅 is more possible
---- 今晚我去吃火锅 with probility 5.012457937326253e-16
---- 今晚火锅去吃我 with probility 1.563034443630964e-18
养乐多绿来一杯 is more possible
---- 洋葱奶昔来一杯 with probility 1.9840875058382383e-20
---- 养乐多绿来一杯 with probility 7.3289295697906e-14
实践
设计自己的句子生成器
###定义两个语法
poem = '''
sentence => sentence1 sentence1 sentence2 sentence2
sentence1 => adj_phrase noun_phrase verb_phrase noun_phrase punctuation
sentence2 => noun verb_phrase noun_phrase adj_phrase noun punctuation
adj_phrase => num unit
noun_phrase => adj noun
verb_phrase => verb
num => 一 | 二 | 三 | 两 | 千 | 万
unit => 行 | 只 | 个 | 声 | 里 | 秋 | 冬
adj => 白 | 黄 | 翠 | 青 | 西 | 东 | 北 | 南
noun => 鹭 | 鹂 | 柳 | 天 | 岭 | 窗 | 雪 | 门 | 吴 | 船
verb => 鸣 | 上 | 含 | 泊
punctuation => ,| 。| ? | !
'''
dynast = '''
sentence2 => dy1 dy2 dy3
dy1 => verb adverb punctuation
dy2 => adj_phrase noun_phrase punctuation
dy3 => noun_phrase adverb noun_phrase adj punctuation
adj_phrase => num unit
noun_phrase => adj noun
verb => 念 | 道 | 悲 | 忆
adverb => 去去 | 沉沉 | 呜呼 | 呼哉 | 凄凄
num => 千 | 万 | 双
unit => 行 | 古 | 里
adj => 烟 | 暮 | 楚 | 阔
noun => 波 | 霭 | 天 | 雪 | 船
punctuation => ,| 。| ? | !
'''
import random
# 根据语法描述 grammar_str 生成规则 grammar
def create_grammar(grammar_str, split='=>', line_split='\n'):
grammar = {}
for line in grammar_str.split(line_split):
if not line.strip(): continue
exp, stmt = line.split(split)
grammar[exp.strip()] = [s.split() for s in stmt.split('|')]
return grammar
# 根据语法规则生成句子
choice = random.choice
def generate(gram, target):
if target not in gram:
return target # means target is a terminal expression #1
expaned = [generate(gram, t) for t in choice(gram[target])] #2
return ''.join([e if e != '/n' else '\n' for e in expaned if e != 'null']) #3
# 定义个函数 使其能够生成n个句子
def generate_n(num):
for i in range(num):
print(generate(gram=create_grammar(poem, split='=>'), target='sentence'))
print(generate(gram=create_grammar(dynast, split='=>'), target='sentence2'))
pass
generate_n(5)
------
千个黄天泊东船!万只青鹭含黄门!雪上翠鹂二里门。门鸣东天三只柳!
道呜呼,万古烟天?楚天呜呼阔天楚。
两冬黄雪泊黄鹭。两里南门鸣白鹂,鹭鸣西岭一冬船,鹭泊翠吴一只吴!
道呜呼。千古阔船,暮霭呼哉阔船暮,
二里青门上东门?二秋翠天含南吴。雪上黄岭一行吴?门含翠鹭两冬船,
念呜呼,千古暮船!楚霭凄凄阔雪烟,
三声黄船上白岭!千冬北鹂鸣青船。鹭上白岭一个门?窗鸣翠雪二冬船,
悲去去。万行楚波,暮天呼哉阔船阔,
万冬南门鸣南窗。两秋东鹂含白吴。船泊东柳一秋窗。柳鸣翠门万只天!
忆沉沉。万里阔天?暮雪沉沉烟天暮!
实列二
# 用于去水果店购买水果的语言
poem = '''
sentence = 主语 谓语 动作 数量 形容词* 物品
主语 = 我 | 我们 | 俺
谓语 = 想 | 需要
动作 = 找 | 称 | 买
数量 = 几斤 | 几个 | 一些
形容词* = null | 形容词 形容词*
形容词 = 大大的 | 圆圆的 | 新鲜的
物品 = 橙子 | 苹果 | 梨子 | 西瓜
'''
dynast = '''
sentence2 = 称呼 报数 . 询问 结尾
称呼 = 敬语 称谓 : 打招呼 ,
敬语 = 尊敬的 | 可爱的
称谓 = 先生 | 女士 | 小朋友
打招呼 = 你好 | 您好
报数 = 我是 数字 号导购员
数字 = 单个数字 | 数字 单个数字
单个数字 = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
询问 = 询问1 | 询问2
询问1 = 请问 动作 物品类别
动作 = 需要买 | 在找
物品类别 = 水果 | 蔬菜 | 肉
询问2 = 今天 产品 形容词 ,来点
产品 = 苹果 | 香蕉 | 青菜
形容词 = 很新鲜 | 很便宜 | 在打折
结尾 = 吗?
'''
import random
# 根据语法描述 grammar_str 生成规则 grammar
def create_grammar(grammar_str, split='=', line_split='\n'):
grammar = {}
for line in grammar_str.split(line_split): # 依次处理语法树的每一行
if not line.strip(): continue # 空行跳过
exp, stmt = line.split(split)
grammar[exp.strip()] = [s.split() for s in stmt.split('|')]
return grammar
# 根据语法规则生成句子
choice = random.choice
def generate(gram, target):
if target not in gram:
return target # means target is a terminal expression #1
expaned = [generate(gram, t) for t in choice(gram[target])] #2
return ''.join([e if e != '/n' else '\n' for e in expaned if e != 'null']) #3
def generate_n(num):
for i in range(num):
print(generate(gram=create_grammar(poem, split='='), target='sentence'))
print(generate(gram=create_grammar(dynast, split='='), target='sentence2'))
pass
generate_n(5)
###另一种输出单行
# 生成购买者和导购者的语法树
seller_grammar_create = create_grammar(seller_grammar)
generate(seller_grammar_create, "seller")
------
俺需要买几斤新鲜的苹果
可爱的先生:你好,我是677264号导购员.今天香蕉很新鲜,来点吗?
我们想买一些西瓜
尊敬的先生:您好,我是47号导购员.今天青菜很便宜,来点吗?
我们需要买几斤圆圆的大大的大大的新鲜的梨子
尊敬的先生:您好,我是2号导购员.今天青菜在打折,来点吗?
我们想买几斤新鲜的新鲜的橙子
可爱的小朋友:您好,我是49号导购员.今天青菜很便宜,来点吗?
俺想找一些大大的新鲜的橙子
可爱的小朋友:您好,我是5号导购员.请问需要买蔬菜吗?
使用新数据源完成语言模型训练
1 下载文本数据集
豆瓣评论数据集:https://raw.githubusercontent.com/Computing-Intelligence/datasource/master/movie_comments.csv
2 修改代码,获得新的2-gram语言模型
进行文本清洗,获得所有纯文本
将这些文本进行切词
送入之前定义的语言模型中,判断文本的合理程度
import pandas as pd
import re
import jieba
from collections import Counter
# 读取文件
filename = 'C:\\Users\dazhi\Desktop\movie_comments.csv'
content = pd.read_csv(filename, encoding='utf-8')
content.head()
# 提取词并写入文件
articles = content['comment'].tolist() # 提取数据集中"content"内容
print(len(articles)) # 输出数据集的长度
# 定义从字符串中提取文本的函数
def token(string):
# 匹配单词字符,即a-z,A-Z,0-9,_
return re.findall('\w+', string)
# 遍历文章内容,token函数实现单词字符的提取
# 观察到数据集中存在很多非中文的文本,这里是对中文句子进行分析,删除过多的英文
articles_clean = [''.join(token(str(a)))for a in articles]
print(len(articles_clean))
# 保存处理好之后的文本
with open('E://nlp/article_movie_comments.txt', 'w', encoding='utf-8') as f:
for a in articles_clean:
f.write(a + '\n')
# 定义中文分词函数
def cut(string):
return list(jieba.cut(string))
TOKEN = [] # 存放分词之后的中文单词
###加载数据集的内容,并将分词后的数据保存到列表中
for i, line in enumerate((open('E://nlp/article_movie_comments.txt','r',encoding='utf-8'))):
if i % 10000 == 0: # 每10000次输出一次
print(i)
if i > 100000:
break
TOKEN += cut(line)
print(TOKEN[0:20]) ##查看数据结果
# 此时已经所有的文章进行了分词,并保存在了TOKEN列表中
# 计算概率
words_count = Counter(TOKEN) # 单词统计
TOKEN = [str(t) for t in TOKEN]
TOKEN_2_GRAM = [''.join(TOKEN[i:i+2]) for i in range(len(TOKEN[:-2]))]
words_count_2 = Counter(TOKEN_2_GRAM)
def prob_1(word):
return words_count[word] / len(TOKEN) # 返回word的统计频率
# count(wk)/(number of words)
###计算概率
def prob_2(word1, word2): # p(w1,w2) = count(w1,2)/count(w1)
if word1 + word2 in words_count_2: return words_count_2[word1+word2] / words_count[word1]
else:
return 1 / len(TOKEN_2_GRAM)
# (w1 w2), (w3,w4) (w4,w5) 2-gram
# (w1,w3) 1/3
def get_probablity(sentence):
words = cut(sentence)
sentence_pro = 1
for i, word in enumerate(words[:-1]):
next_ = words[i+1]
probability = prob_2(word, next_) # p(w1|w2)
sentence_pro *= probability # p(s) = p(w_1)p(w2|w1)*p(w3|w2)..p(wn|wn-1)
return sentence_pro
# 测试句子
print(get_probablity('小明今天抽奖抽到一台苹果手机'))
------
C:\Users\dazhi\AppData\Local\Temp\ipykernel_24812\2689991147.py:8: DtypeWarning: Columns (0,4) have mixed types. Specify dtype option on import or set low_memory=False.
content = pd.read_csv(filename, encoding='utf-8')
261497
261497
0
10000
20000
30000
40000
50000
60000
70000
80000
90000
100000
9.70386170001317e-34
获得最优质的语言
sorted([1, 3, 5, 2])
###接受一个参数key,这个参数接受一个函数作为输入
sorted([(2, 5), (1, 4), (5, 0), (4, 4)], key=lambda x: x[0])
###让list按照第0个元素进行排序.
sorted([(2, 5), (1, 4), (5, 0), (4, 4)], key=lambda x: x[1])
###让list按照第1个元素进行排序.
sorted([(2, 5), (1, 4), (5, 0), (4, 4)], key=lambda x: x[1], reverse=True)
###让list按照第1个元素进行排序, 但是是递减的顺序。
def generate_best(grammar_string, language_model, num): # you code here
sentences = []
for i in range(num):
# 生成句子
sentence = generate(gram=create_grammar(grammar_string, split='=>'), target='sentence')
# 计算概率
probability = language_model(sentence)
sentences.append((sentence, probability))
# 按概率降序排序
sorted(sentences, key=lambda x: x[1], reverse=True)
return sentences[0]
generate_best(poem, get_probablity, 20)
----
('二声南柳泊北船,一里西雪上东柳!吴含南船万冬鹂,门上青吴一个窗!', 2.3794042425808864e-101)
标签:grammar,语言,模型,gram,语法,split,print,line,target
From: https://www.cnblogs.com/idazhi/p/17320887.html