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实现windows下模拟按键和鼠标点击的方法
Mar 13 Python
Python应用03 使用PyQT制作视频播放器实例
Dec 07 Python
Python制作刷网页流量工具
Apr 23 Python
使用 Python 实现微信群友统计器的思路详解
Sep 26 Python
Python 实现文件打包、上传与校验的方法
Feb 13 Python
详解Python的数据库操作(pymysql)
Apr 04 Python
python中class的定义及使用教程
Sep 18 Python
如何获取Python简单for循环索引
Nov 21 Python
Python图像处理库PIL的ImageFilter模块使用介绍
Feb 26 Python
Python装饰器结合递归原理解析
Jul 02 Python
零基础学python应该从哪里入手
Aug 11 Python
pycharm-professional-2020.1下载与激活的教程
Sep 21 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
Warning: session_destroy() : Trying to destroy uninitialized sessionq错误
2011/06/16 PHP
PHP 查找字符串常用函数介绍
2012/06/07 PHP
腾讯QQ微博API接口获取微博内容
2013/10/30 PHP
php 邮件发送问题解决
2014/03/22 PHP
ThinkPHP水印功能实现修复PNG透明水印并增加JPEG图片质量可调整
2014/11/05 PHP
通过php修改xml文档内容的方法
2015/01/23 PHP
PHPStudy下如何为Apache安装SSL证书的方法步骤
2019/01/23 PHP
js中substring和substr的详细介绍与用法
2013/08/29 Javascript
js判断字符是否是汉字的两种方法小结
2014/01/03 Javascript
jQuery Trim去除字符串首尾空字符的实现方法说明
2014/02/11 Javascript
JavaScript中的原型和继承详解(图文)
2014/07/18 Javascript
JS中三目运算符和if else的区别分析与示例
2014/11/21 Javascript
js实现滑动触屏事件监听的方法
2015/05/05 Javascript
jQuery实现购物车表单自动结算效果实例
2015/08/10 Javascript
本地Bootstrap文件字体图标引入却无法显示问题的解决方法
2020/04/18 Javascript
JavaScript字符串对象
2017/01/14 Javascript
nodejs读取图片返回给浏览器显示
2019/07/25 NodeJs
基于 vue-skeleton-webpack-plugin 的骨架屏实战
2019/08/05 Javascript
浅谈vue中$event理解和框架中在包含默认值外传参
2020/08/07 Javascript
vue.js封装switch开关组件的操作
2020/10/26 Javascript
Python中用startswith()函数判断字符串开头的教程
2015/04/07 Python
Python实现数据库编程方法详解
2015/06/09 Python
Python松散正则表达式用法分析
2016/04/29 Python
Python通过format函数格式化显示值
2020/10/17 Python
销售员自我评价怎么写
2013/09/19 职场文书
英语系毕业生自荐信
2013/10/31 职场文书
给水工程专业毕业生自荐信
2014/01/28 职场文书
竞选宣传委员演讲稿
2014/05/24 职场文书
踏青活动策划方案
2014/08/19 职场文书
写给妈妈的感谢信
2015/01/22 职场文书
2015年行政助理工作总结
2015/04/30 职场文书
有关水浒传的读书笔记
2015/06/25 职场文书
2015年秋季学校开学标语
2015/07/16 职场文书
springboot中一些比较常用的注解总结
2021/06/11 Java/Android
如何用python清洗文件中的数据
2021/06/18 Python
Win11使用CAD卡顿或者致命错误怎么办?Win11无法正常使用CAD的解决方法
2022/07/23 数码科技