Python 最大概率法进行汉语切分的方法


Posted in Python onDecember 14, 2018

要求:

1 采用基于语言模型的最大概率法进行汉语切分。

2 切分算法中的语言模型可以采用n-gram语言模型,要求n >1,并至少采用一种平滑方法;

代码:

废话不说,代码是最好的语言

import re
import math

MAX_SPLITLEN = 4#最大切分长度
corpus_lib = ''#corpus:语料


def init_corpus_lib(path): # 初始化语料库
 global corpus_lib
 with open(path, 'r', encoding='utf-8', errors='ignore') as file:
  corpus_lib = str(file.readlines())


def get_candidate_words(sen):
 global MAX_SPLITLEN
 global corpus_lib
 candidate_words = []
 for sp in range(len(sen)):
  w = sen[sp]
  candidate_words.append([w, sp, sp]) # 有些字可能不在语料库中,把它作为单个字加进去
  for mp in range(1, MAX_SPLITLEN): # 判断1 ~ MAX_SPLITLEN-1这3种词中是否有候选词.
   if sp + mp < len(sen):
    w += sen[sp + mp]
    if w in corpus_lib:
     candidate_words.append([w, sp, sp + mp]) # 存储词,初始位置,结束位置
 print('候选词有:%s' % candidate_words)
 return candidate_words


def segment_sentence(sen): # sen:sentence即要切分的句子
 global MAX_SPLITLEN
 global corpus_lib

 candidate_words = get_candidate_words(sen)
 count = 0
 for word in candidate_words:
  if count > 1000: # 为防止对长句子解析时间过长,放弃一部分精度追求效率
   break
  if word[1] == 0 and word[2] != len(sen) - 1: # 如果句子中开头的部分,还没有拼凑成整个词序列的话
   no_whitespace_sen = ''.join(word[0].split())
   for word in candidate_words: # word比如:['今天', 1, 2],1是今在句子中的位置,2是天的位置
    if word[1] == 0 and word[2] != len(sen) - 1:
     end = word[2]
     for later_word in candidate_words:
      if later_word[1] == end + 1: # 如果later_word是当前词的后续词,那么拼接到当前词上
       word_seq = [word[0] + ' ' + later_word[0], word[1], later_word[2]] # 合并
       candidate_words.append(word_seq)
       # print('拼出了新词:%s' % word_seq)
       count += 1
     candidate_words.remove(word) # 遍历完后,这个开头部分短语要移除掉,不然下次遍历还会对它做无用功
 print('所有结果词序列有:%s' % candidate_words)

 word_segment_res_list = [] # 存储分词结果序列
 for seque in candidate_words:
  if seque[1] == 0 and seque[2] == len(sen) - 1:
   word_segment_res_list.append(seque[0])
 print('获得的所有分词结果是:')
 print(word_segment_res_list)
 return word_segment_res_list


# P(w1,w2,...,wn) = P(w1/start)P(w2/w1)P(w3/w2).....P(Wn/Wn-1)
# 下标从0开始: = P(w0/start)P(w1/w0)...P(Wn-1/Wn-2)
def calculate_word_sequence_probability(sequence):
 global corpus_lib
 word_list = sequence.split(' ')
 total_word_num = len(corpus_lib)
 prob_total = 0.0
 word_start = word_list[0]
 # 计算第一个词出现的概率P(w1/start)=Count(w1)/total
 count = len(re.findall(r'\s' + word_start + r'\s', corpus_lib)) + 1 # 加1平滑
 prob_total += math.log(count / total_word_num)
 # 计算P(w2/w1)P(w3/w2).....P(Wn/Wn-1)
 for i in range(len(word_list) - 1): # 0~ n-2
  prev_w = word_list[i]
  later_w = word_list[i + 1]
  count = len(re.findall(r'\s' + prev_w + r'\s' + later_w + r'\s', corpus_lib))
  count += 1 # 做一次加1平滑
  prob_total += math.log(count / total_word_num)
 print('%s的概率是:' % sequence)
 print(prob_total)
 return prob_total


def calculate_biggest_prob(word_segm_res):
 best_w_s = ''
 max_prob = 0.0
 for w_s in word_segm_res: # 改进:先只计算词的数目<=0.6 句子字数的,如果不行再计算全部的概率
  no_whitespace_sen = ''.join(w_s.split())
  zi_shu = len(no_whitespace_sen)
  if len(w_s.split(' ')) <= zi_shu * 0.6:
   prob = calculate_word_sequence_probability(w_s)
   if max_prob == 0 or max_prob < prob:
    best_w_s = w_s
    max_prob = prob
  if best_w_s == '': # 如果上面的0.6不行的话,再计算全部的概率
   prob = calculate_word_sequence_probability(w_s)
   if max_prob == 0 or max_prob < prob:
    best_w_s = w_s
    max_prob = prob
 print('最好的分词结果(概率为%s)是 :%s' % (math.pow(math.e, max_prob), best_w_s))
 return best_w_s


def split_middle(sen_to_segment): # 从中间切分一下,返回中间切分的位置
 length = len(sen_to_segment)
 start = int(length / 2) - 2
 end = start + 5
 # 对中间的5个字进行切分,然后找第一个空格,按此把整个句子一分为二
 middle_part = sen_to_segment[start:end]
 best_segm_res = calculate_biggest_prob(segment_sentence(middle_part))
 return start + best_segm_res.index(' ') - 1


def split_mark_and_too_long_sent(sentences): # 按任意标点符号划分句子,对每个短句进行分词
 sen_list = sentences.splitlines()
 print(sen_list)

 out_text = ''
 for line in sen_list:
  sen_to_segment = '' #
  for single_char in line:
   if single_char.isalpha(): # isalpha()表示是否是单词,如果是单词的为True,标点符号等为False
    sen_to_segment += single_char
   elif not single_char.isalpha() and sen_to_segment == '': # 如果single_char是标点符号、数字,且前面没有待分词的句子
    out_text += single_char + ' '
    print(single_char)

   else: # 如果single_char是标点符号、数字,
    # 如果句子太长,先从中间切分一下
    if len(sen_to_segment) >= 20:
     middle = split_middle(sen_to_segment)
     left_half = sen_to_segment[0:middle + 1] # 左半部分
     best_segm_res = calculate_biggest_prob(segment_sentence(left_half))
     out_text += best_segm_res + ' '
     sen_to_segment = sen_to_segment[middle + 1:len(sen_to_segment)] # 右半部分交给后面几行处理

    best_segm_res = calculate_biggest_prob(segment_sentence(sen_to_segment))
    print(single_char)
    sen_to_segment = ''
    out_text += best_segm_res + ' ' + single_char + ' ' # 标点两侧也用空格隔起来

  # 如果这行句子最后还有一些文字没有切分的话
  if sen_to_segment != '':
   best_segm_res = calculate_biggest_prob(segment_sentence(sen_to_segment))
   out_text += best_segm_res + ' '
  out_text += '\n'

 with open('D:/1佩王的文件/计算语言学基础/生成结果.txt','w') as file:
  file.write(out_text)
 print(out_text)


if __name__ == '__main__':
 path = 'D:/1佩王的文件/计算语言学基础/北大(人民日报)语料库199801.txt'
 init_corpus_lib(path)#初始化语料库

 sentences = ''
 path = 'E:/study/1.研一的课/计算语言学基础课件/testset.txt'#读取要切分的文章
 with open(path, 'r', encoding='gbk', errors='ignore') as file:
  for line in file.readlines():
   sentences += line

 # 改进:先对句子按标点符号划分成多个短句,然后对每个短句进行切分、计算概率
 split_mark_and_too_long_sent(sentences)

实现思路

1、处理语料库

用的是人民日报语料库,然后为了方便把属性去掉了,只留下了词。

2、读要分词的文本,按照标点符号、数字进行分割

按标点符号、数字进行分割,确保分割结果是只有汉字的句子。如果句子过长(>=20),则先对句子中间位置的5个字先切分一次,从5个字的切分结果的第一个空格处,把句子分成两部分,再对每一部分分别切词。标点符号、数字则按照原样输出。

3、找出所有候选词

从一个句子中找出所有的候选词。如每次取4个字,假设为abcd这四个字,得到:a\b\c\d\ab\bc\cd\abc\bcd\abcd,判断它们每个是否在语料库中,如果是的话则存为候选词。并存储下这个词在句子中的开始位置和结束位置。

4、计算出一个句子所有的切分结果

所有的候选词放到了一个python的list(即集合)中,遍历所有开始位置为0但结结束位不为0的候选词,按照词的开始位置和结束位置进行拼凑,新拼凑出的元素会加入到这个list中。当一个词和其他所有能拼凑的词拼凑完后,从list中删除这个词。当遍历结束后,集合中会有长度等于句子长度的元素,这些元素就是一个句子所有的切分结果。

4、使用2-gram模型计算出每种切分结果的概率,挑选出最大概率的句子切分结果

计算概率时使用条件概率,使用加一平滑。条件概率的公式为:P(w1,w2,…,wn) = P(w1/start)P(w2/w1)P(w3/w2)…..P(Wn/Wn-1),利用log把乘法变成加法:log P(w1,w2,…,wn) = log P(w1/start) + logP(w2/w1) + ….. + logP(Wn/Wn-1)

句子往往不是由很多个单字组成的,所以为了提高速度,我们先计算出切分后词个数<= 0.6 * 句子字数的切分结果的概率,如果不为0则返回这个最大概率,如果为0的话,再计算 >= 0.6 的切分结果中的最大概率。

5、将拥有最大概率的句子切分结果存到文件中

以上这篇Python 最大概率法进行汉语切分的方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
手动实现把python项目发布为exe可执行程序过程分享
Oct 23 Python
Python HTMLParser模块解析html获取url实例
Apr 08 Python
用Python的Django框架编写从Google Adsense中获得报表的应用
Apr 17 Python
详解Python中的array数组模块相关使用
Jul 05 Python
浅析Python pandas模块输出每行中间省略号问题
Jul 03 Python
Python实现输入二叉树的先序和中序遍历,再输出后序遍历操作示例
Jul 27 Python
Anaconda之conda常用命令介绍(安装、更新、删除)
Oct 06 Python
python打印文件的前几行或最后几行教程
Feb 13 Python
django的模型类管理器——数据库操作的封装详解
Apr 01 Python
Python实现常见的几种加密算法(MD5,SHA-1,HMAC,DES/AES,RSA和ECC)
May 09 Python
opencv之颜色过滤只留下图片中的红色区域操作
Jun 05 Python
浅谈pytorch中的dropout的概率p
May 27 Python
python实现任意位置文件分割的实例
Dec 14 #Python
pytorch permute维度转换方法
Dec 14 #Python
Python语言快速上手学习方法
Dec 14 #Python
分享Python切分字符串的一个不错方法
Dec 14 #Python
在python中按照特定顺序访问字典的方法详解
Dec 14 #Python
对sklearn的使用之数据集的拆分与训练详解(python3.6)
Dec 14 #Python
python列表list保留顺序去重的实例
Dec 14 #Python
You might like
mysql 搜索之简单应用
2007/04/27 PHP
PHP进程通信基础之信号量与共享内存通信
2017/02/19 PHP
基于jquery的当鼠标滚轮到最底端继续加载新数据思路分享(多用于微博、空间、论坛 )
2011/10/10 Javascript
js 延迟加载 改变JS的位置加快网页加载速度
2012/12/11 Javascript
JS 实现Table相同行的单元格自动合并示例代码
2013/08/27 Javascript
基于jquery步骤进度条源码分享
2015/11/12 Javascript
原生JavaScript实现瀑布流布局
2020/06/28 Javascript
原生javascript实现的ajax异步封装功能示例
2016/11/03 Javascript
原生js和css实现图片轮播效果
2017/02/07 Javascript
JS实现的二叉树算法完整实例
2017/04/06 Javascript
Angular中$broadcast和$emit的使用方法详解
2017/05/22 Javascript
JavaScript实现购物车基本功能
2017/07/21 Javascript
基于vue-cli创建的项目的目录结构及说明介绍
2017/11/23 Javascript
vue单个组件实现无限层级多选菜单功能
2018/04/10 Javascript
React Native日期时间选择组件的示例代码
2018/04/27 Javascript
webpack4.x下babel的安装、配置及使用详解
2019/03/07 Javascript
基于Node的Axure文件在线预览的实现代码
2019/08/28 Javascript
ES6使用新特性Proxy实现的数据绑定功能实例
2020/05/11 Javascript
小程序分享链接onShareAppMessage的具体用法
2020/05/22 Javascript
详解vue父子组件状态同步的最佳方式
2020/09/10 Javascript
[04:48]DOTA2上海特锦赛小组赛第三日 TOP10精彩集锦
2016/02/28 DOTA
在python的WEB框架Flask中使用多个配置文件的解决方法
2014/04/18 Python
python实现从web抓取文档的方法
2014/09/26 Python
详解Python文本操作相关模块
2017/06/22 Python
python 递归遍历文件夹,并打印满足条件的文件路径实例
2017/08/30 Python
Python 访问限制 private public的详细介绍
2018/10/16 Python
python 实现得到当前时间偏移day天后的日期方法
2018/12/31 Python
Python面向对象思想与应用入门教程【类与对象】
2019/04/12 Python
解决python和pycharm安装gmpy2 出现ERROR的问题
2020/08/28 Python
Ray-Ban雷朋瑞典官方网站:全球领先的太阳眼镜品牌
2019/08/22 全球购物
介绍一下write命令
2014/08/10 面试题
师范学院毕业生求职信范文
2013/12/26 职场文书
优秀干部获奖感言
2014/01/31 职场文书
产品发布会策划方案
2014/05/12 职场文书
2015年秋学期教研工作总结
2015/10/14 职场文书
关于Javascript闭包与应用的详解
2021/04/22 Javascript