nlp分词词性标注及命名实体

一个人想着一个人 提交于 2019-11-29 18:55:10

title: nlp分词词性标注及命名实体
date: 2019-08-27 14:26:38
categories: 人工智能
tags:

  • nlp
    cover: https://www.github.com/OneJane/blog/raw/master/小书匠/5f549e01ba4ba668ee78d415b042a010_hd.jpg

nlp分词词性标注及命名实体

分词

中文分词(Chinese Word Segmentation)

指的是将一个汉字序列切分成一个一个单独的词。分词就是将连续的字序列按照一定的规范重新组合成词序列的过程。

词性标注

词性标注(Part-of-Speech tagging 或POS tagging)

又称词类标注或者简称标注,是指为分词结果中的每个单词标注一个正确的词性的程 序,也即确定每个词是名词、动词、形容词或其他词性的过程。在汉语中,词性标注比较简单,因为汉语词汇词性多变的情况比较少见,大多词语只有一个词性,或者出现频次最高的词性远远高于第二位的词性。据说,只需选取最高频词性,即可实现80%准确率的中文词性标注程序。

命名实体识别

命名实体识别(Named Entity Recognition,简称NER)

又称作“专名识别”,是指识别文本中具有特定意义的实体,主要包括人名、地名、机构名、专有名词等。一般来说,命名实体识别的任务就是识别出待处理文本中三大类(实体类、时间类和数字类)、七小类(人名、机构名、地名、时间、日期、货币和百分比)命名实体。 在不同的顷目中,命名实体类别具有不同的定义。

准确分词之加载自定义字典分词

当分词工具分词不准确时,该怎么办? 加载自定义字典?该如何加载?
cut_data.py

# -*- coding=utf8 -*-
import jieba
import re
from tokenizer import cut_hanlp

# 加载字典
jieba.load_userdict("dict.txt")

# 交叉拼接list,把FLAG*替换成*期
def merge_two_list(a, b):
    c = []
    len_a, len_b = len(a), len(b)
    minlen = min(len_a, len_b)
    for i in range(minlen):
        c.append(a[i])
        c.append(b[i])

    if len_a > len_b:
        for i in range(minlen, len_a):
            c.append(a[i])
    else:
        for i in range(minlen, len_b):
            c.append(b[i])
    return c


if __name__ == "__main__":
    fp = open("text.txt", "r", encoding="utf8")
    fout = open("result_cut.txt", "w", encoding="utf8")
	# 特殊符号字典无法分离,正则区分
    regex1 = u'(?:[^\u4e00-\u9fa5()*&……%¥$,,。.@! !]){1,5}期'  # 非汉字xxx期
    regex2 = r'(?:[0-9]{1,3}[.]?[0-9]{1,3})%'  # xx.xx%
    p1 = re.compile(regex1)
    p2 = re.compile(regex2)
    for line in fp.readlines(): # 逐行读取
        result1 = p1.findall(line)  # 返回匹配到的list
        if result1:
            regex_re1 = result1
            line = p1.sub("FLAG1", line)  # 将匹配到的替换成FLAG1
        result2 = p2.findall(line)
        if result2:
            line = p2.sub("FLAG2", line)

        words = jieba.cut(line)  # 结巴分词,type(word)返回一个generator object
        result = " ".join(words)  # 结巴分词结果 本身是一个generator object,所以使用 “ ”.join() 拼接起来

        # E:\hanlp\data\dictionary\custom\resume_nouns.txt在E:\hanlp\hanlp.properties配置
        # CustomDictionaryPath=data/dictionary/custom/CustomDictionary.txt; 现代汉语补充词库.txt; 全国地名大全.txt ns; 人名词典.txt; 机构名词典.txt; resume_nouns.txt; 上海地名.txt ns;data/dictionary/person/nrf.txt nrf;
        words1 = cut_hanlp(line)  # hanlp分词结果,返回的是str
        if "FLAG1" in result:
            result = result.split("FLAG1")
            result = merge_two_list(result, result1)
            ss = result
            result = "".join(result)  # 本身是个list,我们需要的是str,所以使用 "".join() 拼接起来
        if "FLAG2" in result:
            result = result.split("FLAG2")
            result = merge_two_list(result, result2)
            result = "".join(result)
            # print(result)
        fout.write("jieba:" + result)
        fout.write("hanlp:" + words1)
    fout.close()

tokenizer.py

# encoding=utf8
import os, gc, re, sys

from jpype import *

root_path = "E:/hanlp"
djclass_path = "-Djava.class.path=" + root_path + os.sep + "hanlp-1.7.4.jar;" + root_path
startJVM(getDefaultJVMPath(), djclass_path, "-Xms1g", "-Xmx1g")

Tokenizer = JClass('com.hankcs.hanlp.tokenizer.StandardTokenizer')


def to_string(sentence, return_generator=False):
    if return_generator:
        return (word_pos_item.toString().split('/') for word_pos_item in Tokenizer.segment(sentence))
    else:
        return " ".join([word_pos_item.toString().split('/')[0] for word_pos_item in Tokenizer.segment(sentence)])
        # 这里的“”.split('/')可以将string拆分成list 如:'ssfa/fsss'.split('/') => ['ssfa', 'fsss']


def seg_sentences(sentence, with_filter=True, return_generator=False):
    segs = to_string(sentence, return_generator=return_generator)
    if with_filter:
        g = [word_pos_pair[0] for word_pos_pair in segs if
             len(word_pos_pair) == 2 and word_pos_pair[0] != ' ' and word_pos_pair[1] not in drop_pos_set]
    else:
        g = [word_pos_pair[0] for word_pos_pair in segs if len(word_pos_pair) == 2 and word_pos_pair[0] != ' ']
    return iter(g) if return_generator else g


def cut_hanlp(raw_sentence, return_list=True):
    if len(raw_sentence.strip()) > 0:
        return to_string(raw_sentence) if return_list else iter(to_string(raw_sentence))

准确分词之动态调整词频和字典顺序

当分词字典的词冲突,相互影响该怎么办? 调整词频和字典顺序。
cut_data.py

# -*- coding=utf8 -*-
import jieba
import re
from tokenizer import cut_hanlp

jieba.load_userdict("dict.txt")

# # 设置高词频:一个
# jieba.suggest_freq('台中',tune=True)

# 设置高词频:dict.txt中的每一行都设置一下
# fp=open("dict.txt", 'r', encoding='utf8')
# for line in fp:
#     line = line.strip()
#     jieba.suggest_freq(line, tune=True)

# # 设置高词频:dict.txt中的每一行都设置一下快速方法
[jieba.suggest_freq(line.strip(), tune=True) for line in open("dict.txt", 'r', encoding='utf8')]

if __name__ == "__main__":
    string = "台中正确应该不会被切开。"
    # 通过调整词频 suggest_freq(line, tune=True)
    words_jieba = " ".join(jieba.cut(string, HMM=False))
    # 通过排序sort_dict_by_lenth,优先按照长的字典项匹配
    words_hanlp = cut_hanlp(string)
    print("words_jieba:" + words_jieba, '\n', "words_hanlp:" + words_hanlp)

sort_dict_by_lenth.py

# encoding=utf8
import os

dict_file = open(
    "E:" + os.sep + "hanlp" + os.sep + "data" + os.sep + "dictionary" + os.sep + "custom" + os.sep + "resume_nouns.txt",
    'r', encoding='utf8')
d = {}

[d.update({line: len(line.split(" ")[0])}) for line in dict_file]
# 读取源字典文件并从长到短排序 优先匹配长字典项
f = sorted(d.items(), key=lambda x: x[1], reverse=True)
dict_file = open(
    "E:" + os.sep + "hanlp" + os.sep + "data" + os.sep + "dictionary" + os.sep + "custom" + os.sep + "resume_nouns1.txt",
    'w', encoding='utf8')
[dict_file.write(item[0]) for item in f]
dict_file.close()

词性标注代码实现及信息提取

extract_data.py

# -*- coding=utf8 -*-
import jieba
import re
from tokenizer import seg_sentences

fp = open("text.txt", 'r', encoding='utf8')
fout = open("out.txt", 'w', encoding='utf8')
for line in fp:
    line = line.strip()
    if len(line) > 0:
        fout.write(' '.join(seg_sentences(line)) + "\n")
fout.close()
if __name__ == "__main__":
    pass

tokenizer.py

# encoding=utf8
import os, gc, re, sys

from jpype import *

root_path = "E:/hanlp"
djclass_path = "-Djava.class.path=" + root_path + os.sep + "hanlp-1.7.4.jar;" + root_path
startJVM(getDefaultJVMPath(), djclass_path, "-Xms1g", "-Xmx1g")
Tokenizer = JClass('com.hankcs.hanlp.tokenizer.StandardTokenizer')
keep_pos = "q,qg,qt,qv,s,t,tg,g,gb,gbc,gc,gg,gm,gp,m,mg,Mg,mq,n,an,vn,ude1,nr,ns,nt,nz,nb,nba,nbc,nbp,nf,ng,nh,nhd,o,nz,nx,ntu,nts,nto,nth,ntch,ntcf,ntcb,ntc,nt,nsf,ns,nrj,nrf,nr2,nr1,nr,nnt,nnd,nn,nmc,nm,nl,nit,nis,nic,ni,nhm,nhd"
keep_pos_nouns = set(keep_pos.split(","))
keep_pos_v = "v,vd,vg,vf,vl,vshi,vyou,vx,vi"
keep_pos_v = set(keep_pos_v.split(","))
keep_pos_p = set(['p', 'pbei', 'pba'])
drop_pos_set = set(
    ['xu', 'xx', 'y', 'yg', 'wh', 'wky', 'wkz', 'wp', 'ws', 'wyy', 'wyz', 'wb', 'u', 'ud', 'ude1', 'ude2', 'ude3',
     'udeng', 'udh', 'p', 'rr', 'w'])
han_pattern = re.compile(r'[^\dA-Za-z\u3007\u4E00-\u9FCB\uE815-\uE864]+')
HanLP = JClass('com.hankcs.hanlp.HanLP')


def to_string(sentence, return_generator=False):
    if return_generator:
        return (word_pos_item.toString().split('/') for word_pos_item in Tokenizer.segment(sentence))
    else:
        return [(word_pos_item.toString().split('/')[0], word_pos_item.toString().split('/')[1]) for word_pos_item in
                Tokenizer.segment(sentence)]


def seg_sentences(sentence, with_filter=True, return_generator=False):
    segs = to_string(sentence, return_generator=return_generator)
    if with_filter:
        g = [word_pos_pair[0] for word_pos_pair in segs if
             len(word_pos_pair) == 2 and word_pos_pair[0] != ' ' and word_pos_pair[1] not in drop_pos_set]
    else:
        g = [word_pos_pair[0] for word_pos_pair in segs if len(word_pos_pair) == 2 and word_pos_pair[0] != ' ']
    return iter(g) if return_generator else g

TextRank算法原理介绍

tex_rank.py

# -*- coding=utf8 -*-
from jieba import analyse

# 引入TextRank关键词抽取接口 
textrank = analyse.textrank  # 原始文本
text = "非常线程是程序执行时的最小单位,它是进程的一个执行流,\ 是CPU调度和分派的基本单位,一个进程可以由很多个线程组成,\ 线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。\ 线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。\ 同样多线程也可以实现并发操作,每个请求分配一个线程来处理。"
print("\nkeywords by textrank:")  # 基于TextRank算法进行关键词抽取
keywords = textrank(text, topK=10, withWeight=True, allowPOS=('ns', 'n'))

# 输出抽取出的关键词 f
words = [keyword for keyword, w in keywords if w > 0.2]
print(' '.join(words) + "\n")

jieba 词性标注

标注 含义 来源
Ag 形语素 形容词性语素形容词代码为 a,语素代码g前面置以A
a 形容词 取英语形容词 adjective的第1个字母
ad 副形词 直接作状语的形容词形容词代码 a和副词代码d并在一起
an 名形词 具有名词功能的形容词形容词代码 a和名词代码n并在一起
b 区别词 取汉字“别”的声母
c 连词 取英语连词 conjunction的第1个字母
dg 副语素 副词性语素副词代码为 d,语素代码g前面置以D
d 副词 取 adverb的第2个字母,因其第1个字母已用于形容词
e 叹词 取英语叹词 exclamation的第1个字母
f 方位词 取汉字“方”
g 语素 绝大多数语素都能作为合成词的“词根”,取汉字“根”的声母
h 前接成分 取英语 head的第1个字母
i 成语 取英语成语 idiom的第1个字母
j 简称略语 取汉字“简”的声母
k 后接成分
l 习用语 习用语尚未成为成语,有点“临时性”,取“临”的声母
m 数词 取英语 numeral的第3个字母,n,u已有他用
Ng 名语素 名词性语素名词代码为 n,语素代码g前面置以N
n 名词 取英语名词 noun的第1个字母
nr 人名 名词代码 n和“人(ren)”的声母并在一起
ns 地名 名词代码 n和处所词代码s并在一起
nt 机构团体 “团”的声母为 t,名词代码n和t并在一起
nz 其他丏名 “丏”的声母的第 1个字母为z,名词代码n和z并在一起
o 拟声词 取英语拟声词 onomatopoeia的第1个字母
p 介词 取英语介词 prepositional的第1个字母
q 量词 取英语 quantity的第1个字母
r 代词 取英语代词 pronoun的第2个字母,因p已用于介词
s 处所词 取英语 space的第1个字母
tg 时语素 时间词性语素时间词代码为 t,在语素的代码g前面置以T
t 时间词 取英语 time的第1个字母
u 助词 取英语助词 auxiliary
vg 动语素 动词性语素动词代码为 v在语素的代码g前面置以V
v 动词 取英语动词 verb的第一个字母
vd 副动词 直接作状语的动词动词和副词的代码并在一起
vn 名动词 指具有名词功能的动词动词和名词的代码并在一起
w 标点符号
x 非语素字 非语素字只是一个符号,字母 x通常用于代表未知数、符号
y 语气词 取汉字“语”的声母
z 状态词 取汉字“状”的声母的前一个字母
un 未知词 不可识别词及用户自定义词组取英文Unkonwn首两个字母(非北大标准,CSW分词中定义)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!