简化:前面#代表出现的个数,#WiWi+1代表WiWi+1两个词一起出现的个数。
涉及数学知识点:条件概率、独立概率
语句(string)模型的概率(0~1之间)就是每个词在后面词出现的条件下的条件概率的乘积
1-gram就是后面的词(不是一个word,是一个词,一个或多个word)只统计一个的条件概率,2-gram就是后面统计两个的条件概率。
准备数据:
import random
import pandas as pd
filename = '/home/wangwensong/Chapter2.1/sqlResult_1558435.csv'
content = pd.read_csv(filename, encoding='gb18030')
print(content.head())
查看数据量
articles = content['content'].tolist()
print(len(articles))
89611
正则表达式匹配单词(https://mp.csdn.net/editor/html/109505734)
import re
def token(string):
#https://mp.csdn.net/editor/html/109505734
return re.findall('\w+', string)
jieba分词
import jieba
from collections import Counter
#将110条新闻内容分词
with_jieba_cut = Counter(jieba.cut(articles[110]))
#输出粉绿最大的10个词和他们出现的频率
print(with_jieba_cut.most_common()[:10])
运行结果
[(',', 88), ('的', 73), ('。', 39), ('\r\n', 27), ('了', 20), ('们', 18), ('工作队', 16), ('村民', 15), ('收割', 14), ('、', 12)]
打印正则后只剩单词和数字的第110条新闻
print(''.join(token(articles[110])))
运行结果
在外国名著麦田里的守望者中作者想要守护麦田里如自己内心一般纯真的孩子们而驻村干部们也在这个炎热的夏天里撸袖子上阵真正做起了村民们的麦田守望者三夏时节不等人你看到了吗不停翻涌起伏仿若铺陈至天边的金黄麦浪中那若隐若现的人影是自治区新闻出版广电局驻和田市肖尔巴格乡合尼村工作队的队员与工作队组织的青年志愿者在这个炎热的夏季他们深入田间地头帮助村民们收割小麦扛起收麦机麦田中的每个人都显得兴致勃勃一天下来就近22亩小麦收割完毕志愿者麦麦提亚森擦去满脸的汗水高兴地告诉驻村队员我们青年志愿者应该多做贡献为村里的脱贫致富出把力工作队带着我们为村里的老人服务看到那些像我爷爷奶奶一样的老人赞许感谢的目光我体会到了帮助他人的快乐自治区新闻出版广电局驻村工作队孙敏艾力依布拉音麦收时节我们在一起6月中旬的和田墨玉麦田金黄静待收割6月14日15日两天自治区高级人民法院驻和田地区墨玉县吐外特乡罕勒克艾日克村工作队与48名村民志愿者一道帮助村里29户有需要的村民进行小麦收割工作田间地头罕勒克艾日克村志愿队的红旗迎风飘扬格外醒目10余台割麦机一起轰鸣男人们在用机器收割小麦的同时几名妇女也加入到志愿队构成了一道美丽的麦收风景休息空闲工作队员和村民们坐在树荫下田埂上互相问好聊天语言交流有困难就用手势动作比划着聊天有趣地交流方式不时引来阵阵欢笑大家在一同享受丰收和喜悦也一同增进着彼此的情感和友谊自治区高级人民法院驻村工作队周春梅艾地艾木阿不拉细看稻菽千重浪6月15日自治区煤田灭火工程局的干部职工们再一次跋涉1000多公里来到了叶城县萨依巴格乡阿亚格欧尔达贝格村见到了自己的亲戚现场处处都透出掩盖不住的喜悦一声声亲切的谢谢一个个结实的拥抱都透露出浓浓的亲情没坐一会儿在嘘寒问暖中大家了解到在麦收的关键时刻部分村民家中却存在收割难的问题小麦成熟期短收获的时间集中天气的变化对小麦最终产量的影响极大如果不能及时收割会有不小损失的于是大家几乎立刻就决定要帮助亲戚们收割麦子在茂密的麦地里干部们每人手持一把镰刀一字排开挽起衣袖卷起裤腿挥舞着镰刀进行着无声的竞赛骄阳似火汗如雨下但这都挡不住大家的热情随着此起彼伏的镰刀割倒麦子的刷刷声响不一会一束束沉甸甸的麦穗就被整齐地堆放了起来当看到自己亲手收割的金黄色麦穗被一簇簇地打成捆运送到晒场每个人的脸上都露出了灿烂的笑容自治区煤田灭火工程局驻村工作队马浩南这是一个收获多多的季节6月13日清晨6时许和田地区民丰县若雅乡特开墩村的麦田里已经传来马达轰鸣声原来是自治区质监局驻村工作队趁着天气尚且凉爽开始了麦田的收割工作忙碌间隙志愿者队伍搬来清凉的水村民们拎来鲜甜的西瓜抹一把汗水吃一牙西瓜甜蜜的汁水似乎流进了每一个人的心里说起割麦子对于生活在这片土地上的村民来说是再平常不过的事但是对于工作队队员们来说却是陌生的自治区质监局驻民丰县若克雅乡博斯坦村工作队队员们一开始觉得十几个人一起收割二亩地应该会挺快的结果却一点不简单镰刀拿到自己手里割起来考验才真正的开始大家弓着腰弯着腿亦步亦趋手上挥舞着镰刀时刻注意不要让镰刀割到自己脚下还要留心不要把套种的玉米苗踩伤不一会儿就已经汗流浃背了抬头看看身边的村民早就远远地割到前面去了只有今年已经56岁的工作队队长李树刚有割麦经验多少给队员们挽回了些面子赶不上村民们割麦子的速度更不要说搞定收割机这台大家伙了现代化的机械收割能成倍提升小麦的收割速度李树刚说不过能有这样的体验拉近和村民的距离也是很难得的体验自治区质监局驻村工作队王辉马君刚我们是麦田的守护者为了应对麦收新疆银监局驻和田县塔瓦库勒乡也先巴扎村工作队一早就从经济支援和人力支援两方面做好了准备一方面工作队帮村里购入了5台小麦收割机另一边还组织村干部青年团员等组成了6支近百人的收割先锋突击队帮助村民们抢收麦子看着及时归仓的麦子村民们喜得合不拢嘴纷纷摘下自家杏树上的杏子送给工作队金黄的麦穗温暖了村民们的心香甜的杏子温暖了工作队员的心麦子加杏子拉近了村民和队员们的心新疆银监局驻村工作队王继发免责声明本文仅代表作者个人观点与环球网无关其原创性以及文中陈述文字和内容未经本站证实对本文以及其中全部或者部分内容文字的真实性完整性及时性本站不作任何保证或承诺请读者仅作参考并请自行核实相关内容
将所有新闻正则筛选
articles_clean = [''.join(token(str(a)))for a in articles]
由于数据量比较大,先存到一个文件里
with open('article_9k.txt', 'w') as f:
for a in articles_clean:
f.write(a + '\n')
定义切词函数
def cut(string): return list(jieba.cut(string))
所有string切词存起来备用
TOKEN = []
for i, line in enumerate((open('article_9k.txt'))):
if i % 100 == 0: print(i)
#if i > 10000: break
TOKEN += cut(line)
前一百频率画图展示
from functools import reduce
from operator import add, mul
#计算频率
words_count = Counter(TOKEN)
#前一百频率画图展示
frequiences = [f for w, f in words_count.most_common(100)]
x = [i for i in range(100)]
import matplotlib.pyplot as plt
plt.plot(x, frequiences)
运行结果
后面太平了,换log展示
import numpy as np
plt.plot(x, np.log(frequiences))
运行结果
定义一个词的概率函数
def prob_1(word):
return words_count[word] / len(TOKEN)
测试下
print(prob_1('我们'))
定义一个词的概率函数
def prob_1(word):
if word in words_count: return words_count[word] / len(TOKEN)
#如果词没有在训练统计数据中出现过,避免0作被除数
else: return 1/len(TOKEN)
分别创建一个词和两个词的新list备用
#一个词的新list
TOKEN = [str(t) for t in TOKEN]
#两个词的新list
TOKEN_2_GRAM = [''.join(TOKEN[i:i+2]) for i in range(len(TOKEN[:-2]))]
定义两个词的概率函数
words_count_2 = Counter(TOKEN_2_GRAM)
def prob_2(word1, word2):
if word1 + word2 in words_count_2: return words_count_2[word1+word2] / len(TOKEN_2_GRAM)
else:
#0不能做除数,所以...
return 1 / len(TOKEN_2_GRAM)
定义整个句子的概率函数
#计算整个句子的概率
def get_probablity(sentence):
words = cut(sentence)
sentence_pro = 1
end_n=len(sentence)
for i, word in enumerate(words[:-1]):
# 判断最后一个单词
if i == end_n-1:
sentence_pro *=prob_1(word)
break
else:
next_ = words[i + 1]
probability1 = prob_1(word)
probability2 = prob_2(word, next_)
sentence_pro *= probability2/probability1
return sentence_pro
测试下(数据量有点大,一直报错Process finished with exit code 137 (interrupted by signal 9: SIGKILL) ,内存不足,所以没有使用全部数据,只用了8万):
print(get_probablity('目前智能驾驶正在步入订单验证阶段,以摄像头和雷达为主'))
运行结果:
9.672987676059873e-24
语法树生成20句话并计算其概率(5万数据,数据越相关,数据量越大理论上越准确):https://mp.csdn.net/editor/html/113820225
#语法树生成20句话并计算概率
for sen in [generate(gram=create_grammar(host, split='='), target='host') for i in range(20)]:
print('sentence: {} with Prb: {}'.format(sen, get_probablity(sen)))
运行结果:
sentence: 你好我是7号,请问你要打猎吗? with Prb: 4.20824552685305e-24
sentence: 您好我是2号,请问你要喝酒吗? with Prb: 1.1832386008559236e-24
sentence: 您好我是9号,请问你要打猎吗? with Prb: 1.2749010898645874e-24
sentence: 你好我是653号,请问你要赌博吗? with Prb: 1.2102578714891282e-25
sentence: 小朋友,您好我是5号,请问你要打牌吗? with Prb: 1.6045615505266305e-26
sentence: 你好我是73号,请问你要喝酒吗? with Prb: 4.447007281265629e-26
sentence: 你好我是1356号,请问你要打猎吗? with Prb: 1.890422795266019e-23
sentence: 女士,您好我是62351号,请问你要打牌吗? with Prb: 3.1218477987844943e-26
sentence: 女士,你好我是9号,您需要赌博吗? with Prb: 2.068382393757517e-28
sentence: 您好我是9号,您需要打牌吗? with Prb: 5.460122608784673e-24
sentence: 您好我是7号,请问你要赌博吗? with Prb: 2.336647705863692e-25
sentence: 小朋友,您好我是31号,请问你要赌博吗? with Prb: 6.9027932990856e-30
sentence: 女士,您好我是5号,您需要打猎吗? with Prb: 6.232587142796976e-26
sentence: 您好我是5921725号,您需要喝酒吗? with Prb: 5.892549710376718e-23
sentence: 您好我是65号,您需要打猎吗? with Prb: 2.8547756360831296e-24
sentence: 您好我是8号,您需要打猎吗? with Prb: 1.4642155730528248e-23
sentence: 您好我是2号,请问你要喝酒吗? with Prb: 1.1832386008559236e-24
sentence: 你好我是82923号,您需要打猎吗? with Prb: 2.914656295766826e-22
sentence: 先生,您好我是2号,您需要赌博吗? with Prb: 3.657266374747221e-27
sentence: 你好我是5号,请问你要打猎吗? with Prb: 5.4396964044302125e-24
用lambda和sorted按照概率大小进行排序(后期可以选取概率大的语句进行人机对话):
result = []
for sen in [generate(gram=create_grammar(host, split='='), target='host') for i in range(20)]:
result.append([sen, get_probablity(sen)])
result = sorted(result, key=lambda x: x[1], reverse=True)
print(result)
运行结果:
[
['您好我是684号,您需要打猎吗?', 1.1490471935234599e-22],
['您好我是4782841644号,您需要喝酒吗?', 5.892549710376718e-23],
['你好我是661511号,请问你要打牌吗?', 2.1004697725177983e-23],
['你好我是598号,您需要喝酒吗?', 7.473477681453398e-24],
['您好我是3号,请问你要打牌吗?', 4.0148839474699174e-24],
['您好我是3号,您需要赌博吗?', 1.9616682774621595e-24],
['您好我是7号,您需要赌博吗?', 1.8013232182609043e-24],
['您好我是4号,您需要喝酒吗?', 1.1079692341667284e-24],
['你好我是8号,请问你要打牌吗?', 6.691501813377195e-25],
['小朋友,你好我是1号,您需要打猎吗?', 2.366619178314393e-25],
['您好我是6号,请问你要赌博吗?', 2.3214677022003732e-25],
['你好我是3号,请问你要赌博吗?', 1.6136771619855047e-25],
['女士,您好我是1号,您需要打猎吗?', 1.0446719979096046e-25],
['女士,您好我是1号,您需要打牌吗?', 5.803733321720025e-26],
['先生,您好我是5号,您需要打牌吗?', 5.36315892823e-26],
['女士,你好我是97133号,请问你要打牌吗?', 1.979708360204802e-26],
['先生,你好我是9584号,请问你要喝酒吗?', 7.076262448660174e-27],
['小朋友,您好我是9号,请问你要打牌吗?', 2.3847766068312365e-27],
['女士,你好我是478号,您需要赌博吗?', 1.0747594699157418e-27],
['女士,您好我是31号,您需要打牌吗?', 2.350228979060039e-28]
]
整理后的完整代码:
import re
import jieba
import random
import pandas as pd
import numpy as np
from functools import reduce
from operator import add, mul
from collections import Counter
import matplotlib.pyplot as plt
filename = '/home/wangwensong/Chapter2.1/sqlResult_1558435.csv'
content = pd.read_csv(filename, encoding='gb18030')
articles = content['content'].tolist()
def token(string):
#https://mp.csdn.net/editor/html/109505734
return re.findall('\w+', string)
#将所有新闻正则筛选
articles_clean = [''.join(token(str(a)))for a in articles]
#由于数据量比较大,先存到一个文件里
with open('article_9k.txt', 'w') 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('article_9k.txt'))):
if i % 100 == 0: print(i)
if i > 50000: break
TOKEN += cut(line)
#计算频率
words_count = Counter(TOKEN)
#定义一个词的概率函数
def prob_1(word):
if word in words_count: return words_count[word] / len(TOKEN)
#如果词没有在训练统计数据中出现过,避免0作被除数
else: return 1/len(TOKEN)
#一个词的新list
TOKEN = [str(t) for t in TOKEN]
#两个词的新list
TOKEN_2_GRAM = [''.join(TOKEN[i:i+2]) for i in range(len(TOKEN[:-2]))]
#定义word1和word2 一起出现的个数/word2出现的个数
words_count_2 = Counter(TOKEN_2_GRAM)
def prob_2_1(word1, word2):
if word1 + word2 in words_count_2: return words_count_2[word1+word2] / words_count[word2]
else:
#0不能做除数,所以...
return 1 / len(TOKEN_2_GRAM)
#计算整个句子的概率
def get_probablity(sentence):
words = cut(sentence)
sentence_pro = 1
end_n=len(sentence)
for i, word in enumerate(words[:-1]):
next_ = words[i + 1]
probability = prob_2_1(word,next_)
sentence_pro *= probability
sentence_pro*=prob_1(words[-1])
return sentence_pro
#测试,语法树 https://mp.csdn.net/editor/html/113820225 ##################################################################
#一个“接待员”的语言可以定义为
host = """
host = 寒暄 报数 询问 业务相关 结尾
报数 = 我是 数字 号 ,
数字 = 单个数字 | 数字 单个数字
单个数字 = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
寒暄 = 称谓 打招呼 | 打招呼
称谓 = 人称 ,
人称 = 先生 | 女士 | 小朋友
打招呼 = 你好 | 您好
询问 = 请问你要 | 您需要
业务相关 = 玩玩 具体业务
玩玩 = null
具体业务 = 喝酒 | 打牌 | 打猎 | 赌博
结尾 = 吗?
"""
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
expaned = [generate(gram, t) for t in choice(gram[target])]
return ''.join([e if e != '/n' else '\n' for e in expaned if e != 'null'])
########################################################################################################################
#语法树生成20句话并计算概率
result = []
for sen in [generate(gram=create_grammar(host, split='='), target='host') for i in range(20)]:
result.append([sen, get_probablity(sen)])
result = sorted(result, key=lambda x: x[1], reverse=True)
print(result)