【NLP】基于DP算法的中文分词系统(Incorporate Semantic )

.
.
.

目前在学习NLP 在学习分词时自己做的一个案例.参考维特比算法.
下面是整个系统的流程图.
在这里插入图片描述

  1. 首先,将原始文本,通过正则表达式根据非中文字符进行拆分。得到一个句子的列表,然后分别对每一个句子进行分词处理。

  2. 对于单个句子,首先进行按照不同的字长进行划分,如单字,双字和三字等等放入汉字列表并去除重复元素。

  3. 加载词库,将获得的汉字列表,在词库中进行匹配. .如果存在,则将权值设置为字频的倒数.存入字典中,格式如下{‘我’:1/f,’去一’:1000}.

  4. 下面以句子中每一个单词为结点以字典为对应权值建立有向网.

  5. 使用动态规划求出最短路径

  6. 将最短路径转化为汉字.

案例说明:

待分词句子:

sentence="经常有意见分歧"

可能分词

['经', '常', '有', '意', '见', '分', '歧', '经常', '常有', '有意', '意见', '见分', '分歧', 
'歧', '经常有', '常有意', '有意见', '意见分', '见分歧', '分歧', '歧', '经常有意', 
'常有意见', '有意见分', '意见分歧', '见分歧', '分歧', '歧', '经常有意见', '常有意见分', 
'有意见分歧', '意见分歧', '见分歧', '分歧', '歧']

在字典中查找分词:生成字典

{"经常":0.23, "经":0.3, "有":0.23, "有意见":0.23,"意见":0.16,'常':1000,'歧':1000,
"分歧":0.16,"见":0.3,"意":0.3,"见分歧":0.3, "分":0.23,"我":0.16}

构造有向网如下图所示.
在这里插入图片描述
该图的逆邻接表

{
1: [], 
2: [[1, 0.3]], 
3: [[2, 1000], [1, 0.23]], 
4: [[3, 0.23]], 
5: [[4, 0.3]], 
6: [[5, 0.3], [3, 0.23], [4, 0.16]], 
7: [[6, 0.23]], 
8: [[7, 1000], [6, 0.16], [5, 0.3]]
}

构造path 和cost表
path表

节点12345678
下一跳01134366

cost表

节点12345678
费用00. 30. 230. 460. 760.460.690. 62

则最短路径如上图,绿色部分所示.
即为 8–>6–>3—>1

1 -经- 2 -常- 3 -有- 4 -意- 5 -见- 6 -分- 7 -歧- 8

1-3对应 [经常]
3-6:对应[有意见]
6-8: 对应[分歧]
所以结果为

[‘经常’,’有意见’,’分歧’].

具体代码实现

条目内容
开发环境Ubuntu+Python3
运行环境Ubuntu+Python3
涉及的库函数Pandas numpy re
  1. 拆分句子:
    使用re模块的split方法,将所有的特殊符号作为分隔符,返回只有汉字的列表,共以下函数使用.
p=re.compile("[a-z,\-。、·:,【】|、\d — “!@$^&*”!#%……&×()  \n \t \b~]",re.M)
lis_sen=[ x for x in re.split(p,text) if x!=""]
  1. 对单个句子使用不同的字长,划分为词组列表.
def devide(self,s):
		a=[]
		for j in range(1,self.max_step+1):#max_step为步长
			c=[]
			for i in range(len(s)):
				c.append(s[i:i+j])
			a=a+c
		return a
  1. 加载词库并根据单词过滤:读取res目录下的dict.csv 文件.
def load_ciku(self):
	data=pandas.read_csv("../res/dict_.csv",sep='\t')
	data_=numpy.array(data)
	print("加载词库....")
	dic=[x[0].split(',') for x in data_]
	print("解析词库....")
	dic={x[0]:x[1].split(' ')[0] for x in dic}
	return dic

  1. 使用dict_filter()方法,在词库中提取所有词组列表中含有的单词,并添加权值,构造一个新的字典.
def dict_filter(self,sentence):
		res=self.devide(sentence)
		dic=self.dic
		dictory={}
		for x in res:
			dictory[x]=1000
			if dic.__contains__(x):
				dictory[x]=1/int(dic[x])
				if len(x)==1:
					dictory[x]*=self.offset#消除单字过分的影响。
		return dictory

以句子中的每个汉字作为节点,词组字典的权值,建立有向网,主要分为两个部分,
1.将单字符词组构建主干线路.将每个汉字根据句子顺序链接
2.将多字符字符词组添加到图中

	def creat_words_graph(self,sentence,dictory):
		length=len(sentence)
		words_graph={x+1 : [] for x in range(length+1)}
		for i in range(2,length+2):
			distance=1000
			if dictory.__contains__(sentence[i-2]):
				distance=dictory[ sentence[i-2] ]
			words_graph[i].append( [i-1, distance ]  )
		dict_=[list(x) for x in list(dictory.items())]
		for x in dict_:
			if len(x[0])==1:
				continue
			start=0
			while sentence.index( x[0][-1],start)+2< sentence.index(x[0][0]) +1:
				#由重复关键字造成的异常.使用 start去掉不合法的部分
				start=sentence.index( x[0][-1],start)+1
			words_graph[sentence.index( x[0][-1],start)+2].append([sentence.index(x[0][0]) +1, x[1] ])
		return words_graph
  1. 计算根据动态规划最短路径.
def get_shorts_path(self,sentence,words_graph):
		length=len(sentence)
		cost=[0 for x in range(length+1)]
		path=[0 for x in range(length+1)]
		path[0]=-1
		for i in range(1,length+1):
			cost[i],path[i]=self.get_min_cost(words_graph[i+1],cost)
			# get_min_cost 得到当前节点最适合的路径
		path=[i+1 for i in path ]
		short_path=[]
		i=length
		short_path.append(i+1)
		while path[i] !=0:
			node=path[i]
			short_path.append(node)
			i=node-1
		short_path.reverse()
		return short_path

测试用例.

INPUT[   ] : 其中包括对饮食材料与烹调方法以及烹调风味及佐料的偏好 
RES  [   ] :  ['其中', '包括', '对', '饮食', '材料', '与', '烹调', '方法', '以及', '烹', '调', '风味', '及', '佐料', '的', '偏好'] 
INPUT[   ] : 我超级喜欢编程写代码,一天都不会感觉疲惫
RES  [   ] :  ['我', '超级', '喜欢', '编程', '写', '代码', '一', '天都', '不会', '感觉', '疲惫']
INPUT[   ] : 哈尔滨,简称“哈”,古称会宁府、阿勒锦,别称冰城,是黑龙江省省会
RES  [   ] : ['哈尔滨', '简称', '哈', '古称', '会宁', '府', '阿勒锦', '别称', '冰城', '是', '黑龙江', '省省会']

源码地址:github

到此,各位少侠的word segmentation 就介绍到这里。

代码江湖漫漫,少侠下期再见!

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值