python实现词语填空_python简单实现新词发现
基于新信息熵的新词发现原理《互联网时代的社会语言学:基于SNS的文本数据挖掘》这篇文章已经讲得非常清楚了,在这里主要是通过代码复现这篇文章。
实现的模块主要分为四个部分:从文章中提取所有可能出现的候选词。
计算每一个词的聚合度。
计算每一个词的左临熵和右临熵,即:自由度。
通过聚合度和左右临熵的分值组合来对一个候选词进行打分。
一、提取所有候选词
在我们对词没有概念的情况下我们会对文章的所有可能出现的词进行统计。比如一句话为“天气预报说周五会下雨”,要找出这一句话中的候选词我们可能会说“天”、“天气”、“天气预”等等都可能是一个词,那么这种没有限制的最长词的情况下在短短的10字的句子中可能会出现10!种可能,按照常识,我们需要预先设定一个最长词的长度。比如我们设置最长的词长度为5,那么这一句话中的所有候选词就为:“天”、“天气”、“天气预”、"天气预报"、"天气预报说"、"气"、"气预"、"气预报"、"气预报说"、"气预报说今"......等等,有5+5+5+5+5+5+4+3+2+1=40个候选词。
以下为提取候选词的代码:
def find_words(self, doc):
'''
找出所有可能出现的词, doc为传进去的文本
:param doc:
:param max_len_word:
:return:
'''
len_doc = len(doc)
for i in range(len_doc):
for j in range(i + 1, i + self.max_len_word + 1):
if doc[i:j] in self.words:
self.words[doc[i:j]]['freq'] += 1
else:
self.words[doc[i:j]] = {}
self.words[doc[i:j]]['freq'] = 1
二、计算聚合度
文章用“电影院”成词这个例子来讲聚合度,作者统计了在整个2400万字的数据中“电影”一次出现了2774次,出现的概率为0.000113, “院”字出现了4797次,出现的概率为0.0001969,如果两者间真的毫无关系的话,他们拼接在一起(电影院)的概率为P(电影)×P(院)/2,但其实“电影院"一共出现175次,要远远高于两个词的概率的乘积,是P(电影)×P(院)/2的600多倍,还统计了"的"字出现的概率为0.0166,并且文章中出现的“的电影”的真实概率P(的电影)与P(的)×P(电影)/2很接近,所以表明“电影院”更可能是一个有意义的搭配,而“的电影”则更像是“的”和“电影”两个成分偶然拼接到一起的。
在实现的过程中我们首先需要计算所有候选词出现的概率,然后通过概率计算每一个词的聚合度。那上边的例子来说(令dop表示聚合度):
“天气预报说周五会下雨”
其中令单字的聚合度为0
则dop(天)=0
dop(天气)=P(天)×P(气)
dop(天气预)=P(天)×P(气预)+P(天气)×P(预)
dop(天气预报)=P(天)×P(气预报)+P(天气)×P(预报)+P(天气预)×P(报)
dop(天气预报说)=P(天)×P(气预报说)+P(天气)×P(预报说)+P(天气预)×P(报说)+P(天气预报)×P(说)
......
也就是对词进行二切分,然后切分后的概率的乘积,在这里我去了每一个二切分的概率乘积的和,其实也可以用另一种方法:“电影院”的凝合程度则是 p(电影院) 分别除以 p(电) · p(影院) 和 p(电影) · p(院) 所得的商的较小值,这样处理甚至会有更好的效果,因为用最小值来代表这个词的聚合度,更能有力的证明该词的成词性,及该词的聚合度最小的情况下都成词的话,那么这个词肯定成词。
算出每隔词的聚合度后对每一个聚合度求log,具体为什么要求log后边会提到。
代码实现聚合度如下:
def dop(self):
'''
计算聚合度
:param words:
:return:
'''
len_words = len(self.words)
# 计算每一个词频
for k, v in self.words.items():
self.words[k]['freq_radio'] = self.words[k]['freq']/(5 * len_words)
for k, v in self.words.items():
dop = []
l = len(k)
if l == 1:
self.words[k]['dop'] = 0
else:
for i in range(1, l):
word = self.words[k[0:i]]['freq_radio']*self.words[k[i:l]]['freq_radio']
dop.append(word)
dop = sum(dop)
self.words[k]['dop'] = math.log(self.words[k]['freq_radio']/dop)
三、自由度
光看文本片段内部的凝合程度还不够,我们还需要从整体来看它在外部的表现。考虑“被子”和“辈子”这两个片段。我们可以说“买被子”、“盖被子”、“进被子”、“好被子”、“这被子”等等,在“被子”前面加各种字;但“辈子”的用法却非常固定,除了“一辈子”、“这辈子”、“上辈子”、“下辈子”,基本上“辈子”前面不能加别的字了。“辈子”这个文本片段左边可以出现的字太有限,以至于直觉上我们可能会认为,“辈子”并不单独成词,真正成词的其实是“一辈子”、“这辈子”之类的整体。可见,文本片段的自由运用程度也是判断它是否成词的重要标准。如果一个文本片段能够算作一个词的话,它应该能够灵活地出现在各种不同的环境中,具有非常丰富的左邻字集合和右邻字集合。
我们用信息熵来衡量一个文本片段的左邻字集合和右邻字集合有多随机。考虑这么一句话“吃葡萄不吐葡萄皮不吃葡萄倒吐葡萄皮”,“葡萄”一词出现了四次,其中左邻字分别为 {吃, 吐, 吃, 吐} ,右邻字分别为 {不, 皮, 倒, 皮} 。根据公式,“葡萄”一词的左邻字的信息熵为 – (1/2) · log(1/2) – (1/2) · log(1/2) ≈ 0.693 ,它的右邻字的信息熵则为 – (1/2) · log(1/2) – (1/4) · log(1/4) – (1/4) · log(1/4) ≈ 1.04 。可见,在这个句子中,“葡萄”一词的右邻字更加丰富一些。
在实现的过程中首先遍历每一个候选词,然后对找出该候选词在文章的所有位置,然后找出该候选词前边一个字符出现的所有情况,然后就根据公式计算该候选词的做临熵,右临熵类似,代码如下:
def left_free(self, doc):
'''
计算左自由度
:param words:
:return:
'''
for k, v in self.words.items():
left_list = [m.start() for m in re.finditer(k, doc) if m.start() != 1]
len_left_list = len(left_list)
left_item = {}
for li in left_list:
if doc[li-1] in left_item:
left_item[doc[li-1]] += 1
else:
left_item[doc[li-1]] = 1
left = 0
for _k, _v in left_item.items():
left += abs((left_item[_k]/len_left_list) * math.log(1/len(left_item)))
self.words[k]['left_free'] = left
def right_free(self, doc):
'''
计算右自由度
:param words:
:return:
'''
for k, v in self.words.items():
right_list = [m.start() for m in re.finditer(k, doc) if m.start() < len(doc)-5]
len_right_list = len(right_list)
right_item = {}
for li in right_list:
if doc[li+len(k)] in right_item:
right_item[doc[li+len(k)]] += 1
else:
right_item[doc[li+len(k)]] = 1
right = 0
for _k, _v in right_item.items():
right += abs((right_item[_k]/len_right_list) * math.log(1/len(right_item)))
self.words[k]['right_free'] = right
四、通过聚合度和左右临熵的分值组合来对一个候选词进行打分
知道了聚合度和自由度,就可以对一个词进行综合打分,我再这里用的简单粗暴的相加(聚合度+自由度),然后使用dataframe做了个排序。
代码如下:
def get_df(self):
df = pd.DataFrame(self.words)
df = df.T
df['score'] = df['dop'] + df['left_free'] + df['right_free']
df = df.sort_values(by='score', ascending=False)
df = df[df['score'] > self.radio]
return df
完整代码如下:
# -*- coding: utf-8 -*-
import re
import math
import pandas as pd
import string
from collections import OrderedDict
class NewWord(object):
def __init__(self, max_len_word, radio):
self.max_len_word = max_len_word
self.radio = radio
self.words = {}
def find_words(self, doc):
'''
找出所有可能出现的词
:param doc:
:param max_len_word:
:return:
'''
len_doc = len(doc)
for i in range(len_doc):
for j in range(i + 1, i + self.max_len_word + 1):
if doc[i:j] in self.words:
self.words[doc[i:j]]['freq'] += 1
else:
self.words[doc[i:j]] = {}
self.words[doc[i:j]]['freq'] = 1
def dop(self):
'''
计算聚合度
:param words:
:return:
'''
len_words = len(self.words)
# 计算每一个词频
for k, v in self.words.items():
self.words[k]['freq_radio'] = self.words[k]['freq']/(5 * len_words)
for k, v in self.words.items():
dop = []
l = len(k)
if l == 1:
self.words[k]['dop'] = 0
else:
for i in range(1, l):
word = self.words[k[0:i]]['freq_radio']*self.words[k[i:l]]['freq_radio']
dop.append(word)
dop = sum(dop)
self.words[k]['dop'] = math.log(self.words[k]['freq_radio']/dop)
def left_free(self, doc):
'''
计算左自由度
:param words:
:return:
'''
for k, v in self.words.items():
left_list = [m.start() for m in re.finditer(k, doc) if m.start() != 1]
len_left_list = len(left_list)
left_item = {}
for li in left_list:
if doc[li-1] in left_item:
left_item[doc[li-1]] += 1
else:
left_item[doc[li-1]] = 1
left = 0
for _k, _v in left_item.items():
left += abs((left_item[_k]/len_left_list) * math.log(1/len(left_item)))
self.words[k]['left_free'] = left
def right_free(self, doc):
'''
计算右自由度
:param words:
:return:
'''
for k, v in self.words.items():
right_list = [m.start() for m in re.finditer(k, doc) if m.start() < len(doc)-5]
len_right_list = len(right_list)
right_item = {}
for li in right_list:
if doc[li+len(k)] in right_item:
right_item[doc[li+len(k)]] += 1
else:
right_item[doc[li+len(k)]] = 1
right = 0
for _k, _v in right_item.items():
right += abs((right_item[_k]/len_right_list) * math.log(1/len(right_item)))
self.words[k]['right_free'] = right
def get_df(self):
df = pd.DataFrame(self.words)
df = df.T
df['score'] = df['dop'] + df['left_free'] + df['right_free']
df = df.sort_values(by='score', ascending=False)
df = df[df['score'] > self.radio]
return df
def run(self, doc):
doc = re.sub('[,,.。"“”‘’\';;::、??!!\n\[\]\(\)()\\/a-zA-Z0-9]', '', doc)
self.find_words(doc)
self.dop()
self.left_free(doc)
self.right_free(doc)
df = self.get_df()
return df
if __name__ == '__main__':
doc = open('./model/data.txt', 'r', encoding='utf-8').read()
nw = NewWord(max_len_word=5, radio=9.6)
df = nw.run(doc)
df.to_csv('./model/text.txt', sep='|', encoding='utf-8')
python实现词语填空_python简单实现新词发现相关推荐
- python爬取图片实例_Python简单爬取图片实例
都知道Python的语法很简单易上手,也很适合拿来作爬虫等等,这里就简单讲解一下爬虫入门--简单地爬取下载网站图片.html 效果 就像这样自动爬取下载图片到本地:python 代码: 其实很简单,咱 ...
- python图像验证码识别_python 简单图像识别--验证码
python 简单图像识别--验证码 记录下,准备工作安装过程很是麻烦. 首先库:pytesseract,image,tesseract,PIL windows安装PIL,直接exe进行安装更方便( ...
- python语句块标记_Python简单语句
简单语句由(逻辑上的)一行组成. 1.表达式语句 表达式也可以是语句.如果表达式是函数调用或者文档字符串的话尤其有用. 例如: "This modue contains SPAM-relat ...
- python读取json配置文件_Python简单读取json文件功能示例
本文实例讲述了Python简单读取json文件功能.分享给大家供大家参考,具体如下: read_json.json: { "rule":{ "namespace" ...
- python字典删除元素_Python简单遍历字典及删除元素的方法
本文实例讲述了Python简单遍历字典及删除元素的方法.分享给大家供大家参考,具体如下: 这种方式是一定有问题的: d = {'a':1, 'b':2, 'c':3} for key in d: d. ...
- python分子化学模拟_python简单实现gillespie模拟
由于专业需求,需要做主方程的随机模拟.在网上并没有找到适合的Python实现,遂自己写了一个,分享一下源码.至于gillespie算法本身就不介绍了,有需要的读者自然会懂,没需要的读者不建议去懂. 源 ...
- python web框架 多线程_python 简单web框架: Bottle
基本映射 映射使用在根据不同URLs请求来产生相对应的返回内容.Bottle使用route() 修饰器来实现映射. 1 2 3 4 5 from bottle import route, run@ro ...
- python sort 部分元素_Python 简单排序算法-选择、冒泡、插入排序实现
写文章主要是记录自己每天学习的东西,本篇文章主要介绍数据结构中常用的简单的排序算法,虽然这些算法用Python实现起来不是十分的高效,不如c.java之类的运行速度快,应用Python实现主要是为了: ...
- python 百度百科 爬虫_python简单爬虫
爬虫真是一件有意思的事儿啊,之前写过爬虫,用的是urllib2.BeautifulSoup实现简单爬虫,scrapy也有实现过.最近想更好的学习爬虫,那么就尽可能的做记录吧.这篇博客就我今天的一个学习 ...
最新文章
- Node.js项目实践:构建可扩展的Web应用
- Gartner:企业架构开始更加紧密地向业务看齐
- centeros7安装docker
- MM模块几个移动类型之间的区别
- ts16949 软件开发流程图_新产品开发流程图:包括APQP,DFMEA,PFMEA,PPAP(FAI),SPC,MSA(GRR)等...
- Android webview数据获取 webview抓取
- 【Python】绘制空气质量日历图
- 【云锁·nginx自编译web防护教程】
- JS实现文档下载兼容谷歌、火狐、IE
- .Net部分 面试问题
- 面向商业市场,华为式“抢滩登陆”
- char、nchar、varchar、nvarchar的区别和特点
- 多線程之WaitFor
- 我比较笨,我得一步一步来
- 如何逃避classin专注学习模式
- BQB 蓝牙无线技术
- [1184]FinalShell下载安装
- UE4源码调试--配置VS环境
- Python的判及格与否
- 第一章(第一节):数、式、方程与方程组
热门文章
- Linux内核实验作业四
- ASP.net实现邮件发送
- 如何解决Asp.Net MVC和WebAPI的Controller名称不能相同的问题
- redis启动异常处理一例
- 织梦最新版后台一键更新网站、更新文档HTML卡死的解决方法
- 如何使用jQuery替换div的innerHTML?
- 使用GitHub存储库中的更改更新本地存储库
- 创建零填充JavaScript数组的最有效方法?
- 一个或多个实体的验证失败。 有关更多详细信息,请参见“ EntityValidationErrors”属性
- JAVA 多用户商城系统b2b2c-服务容错保护(Hystrix依赖隔离)