前置知识:NER 命名实体识别,re正则表达式,jieba分词(只描述一种思想,代码并不复杂,这些前置知识在代码实现的时候会进行解读) # 最后有未注释总代码可直接复制 #
本次实现的主要功能
实现一个使用规则来识别一句话中的实体
识别这段文本“山东省齐鲁工业大学,坐落于山东省济南市,拥有多个校区.山东省济南大学,同样也坐落于山东省济南市.他们都是非常棒的大学”
返回一个存放两个大学名称的列表['山东省齐鲁工业大学','山东省济南大学']
主要分为两部分实现:1.设定一个领域字典 2.根据词性分词 3.设定正则表达式 4.根据正则表达式输出结果
1.导入依赖包+设定领域字典+目标文本
(领域字典就是公司实体会结束的名字)
import re
import jieba
import jieba.posseg as pseg
# 可以根据需求自行添加
org_tag = ['大学']
text = "山东省齐鲁工业大学,坐落于山东省济南市,拥有多个校区.山东省济南大学,同样也坐落于山东省济南市.他们都非常棒"
2.根据词性分词
#pseg在jieba分词中 可以根据标注词性进行分词 lcut是分词后以列表形式返回
words_flags = pseg.lcut(text)
print(f'words_flags->{words_flags}')
words, features = [], []
for word,feature in words_flags:
#对words_flags进行解包,获取词以及对应的词性 把所有词加入words列表中
words.append(word)
# 如果这个词在领域词典中,就代表实体的结束,在features中标为E
if word in org_tag:
features.append('E')
# ns在jieba分词中代表地名,如果这个词性是地名,就代表实体的开始,在features中标为S
elif feature == 'ns':
features.append('S')
# 其他的所有在features中标为O 不是数字0是大写字母O
else:
features.append('O')
print(f'words->{words}')
print(f'features->{features}')
# 使用空字符把features这个列表连成字符串
labels = ''.join(features)
print(f'labels->{labels}')
输出结果:(可以对比查看一下)
words_flags->[pair('山东省', 'ns'), pair('齐鲁', 'nr'), pair('工业', 'n'), pair('大学', 'n'), pair(',', 'x'), pair('坐落于', 'v'), pair('山东省', 'ns'), pair('济南市', 'ns'), pair(',', 'x'), pair('拥有', 'v'), pair('多个', 'm'), pair('校区', 'n'), pair('.', 'x'), pair('山东省', 'ns'), pair('济南', 'ns'), pair('大学', 'n'), pair(',', 'x'), pair('同样', 'd'), pair('也', 'd'), pair('坐落于', 'v'), pair('山东省', 'ns'), pair('济南市', 'ns'), pair('.', 'x'), pair('他们', 'r'), pair('都', 'd'), pair('是', 'v'), pair('非常', 'd'), pair('棒', 'a'), pair('的', 'uj'), pair('大学', 'n')]
words->['山东省', '齐鲁', '工业', '大学', ',', '坐落于', '山东省', '济南市', ',', '拥有', '多个', '校区', '.', '山东省', '济南', '大学', ',', '同样', '也', '坐落于', '山东省', '济南市', '.', '他们', '都', '非常', '棒']
features->['S', 'O', 'O', 'E', 'O', 'O', 'S', 'S', 'O', 'O', 'O', 'O', 'O', 'S', 'S', 'E', 'O', 'O', 'O', 'O', 'S', 'S', 'O', 'O', 'O', 'O', 'O']
labels->SOOEOOSSOOOOOSSEOOOOSSOOOOO
3.设置正则表达式
这一步没有输出,具体的匹配内容以及finditer返回格式会在下一步输出中
# 我们想得到的就是 地名开头S 中间随意O 结尾为领域字典中的E
# 设置一个正则表达式 S+是S出现最少一次 O*是O出现任意次 E+是E至少出现一次
patten = re.compile('S+O*E+')
# 使用finditer 根据patten规则 匹配由SOE组成的字符串labels
# 这里的finditer 返回一个迭代器,每次迭代都会返回一个匹配对象 其中包含起始和结束索引
finall_labels = re.finditer(patten, labels)
4.迭代正则的匹配内容,并处理为最终结果
# 定义空列表存储最终结果
list1= []
for ne in finall_labels:
print(ne)
# 获取匹配对象中的 起始和结束索引
start = ne.start()
end = ne.end()
print(f'start->{start}')
print(f'end->{end}')
# 根据索引来对列表words切片 获取结果 并使用join转为字符串
print(''.join(words[start: end]))
#将结果加入list1中
list1.append(''.join(words[start: end]))
print(f'最终结果->{list1}')
输出结果:可以看到for每次迭代finditer的返回结果,其中的span就是起始和结束索引,使用 .start以及.end就可以获取 并且获取到的结束索引要后一位(ex:匹配索引为0,1,2,3 返回的起始和结束索引就是0,4)这样在后续切片也是左闭右开,非常银性哈
<re.Match object; span=(0, 4), match='SOOE'>
start->0
end->4
山东省齐鲁工业大学
<re.Match object; span=(13, 16), match='SSE'>
start->13
end->16
山东省济南大学
最终结果->['山东省齐鲁工业大学', '山东省济南大学']
纯净代码纯享版(可以直接复制使用没有多余的输出和注释)
# coding: utf-8
import re
import jieba
import jieba.posseg as pseg
org_tag = ['大学']
text = "山东省齐鲁工业大学,坐落于山东省济南市,拥有多个校区.山东省济南大学,同样也坐落于山东省济南市.他们都非常棒"
words_flags = pseg.lcut(text)
words, features = [], []
for word,feature in words_flags:
words.append(word)
if word in org_tag:
features.append('E')
elif feature == 'ns':
features.append('S')
else:
features.append('O')
labels = ''.join(features)
patten = re.compile('S+O*E+')
finall_labels = re.finditer(patten, labels)
list1= []
for ne in finall_labels:
start = ne.start()
end = ne.end()
list1.append(''.join(words[start: end]))
print(f'最终结果->{list1}')
之后还会有主流的BiLSTM+CRF,敬请期待