NLP实践——基于SIFRank的英文关键短语抽取
1. 回顾
之前的文章中介绍了如何用SIFRank进行中文的关键词抽取:
https://blog.csdn.net/weixin_44826203/article/details/121144724
有读者问到是否可以用来做英文,答案是肯定的,SIFRank原本就是做英文的,自然可以采用类似的方法进行改写,使之可以适用于英文。所以这篇文章就对之前我改写的代码进行一点补充,在之前的代码基础上增加对英文场景下的关键词抽取。
所以如果你还没有看过之前那篇文章,请先点击上面的链接跳转,对整个流程有一个大体的了解。
2. 英文关键词抽取
上次将关键词抽取模型梳理成以下几个组件:
- utils
- 标点和停用词
- 预训练词汇权重
- 分词/词性标注模型
- 候选短语抽取模型
- 词形还原模型
- 编码模型
打对号的是这次不需要进行修改的,剩下的组件或多或少都需要进行一点修改。
2.1 预训练词汇权重
这个其实就是一个词频统计,可以在自己的语料上进行统计。原作者在项目中给出了几个可以用的词频统计结果,通过下方链接跳转到git地址:
https://github.com/sunyilgdx/SIFRank
下载其中auxiliary_data目录下的wiki文件,放在我们目录下resources中,目录结构参考中文关键短语抽取博客。
然后替换原来的目录即可:
weightfile_pretrain = './resources/enwiki_vocab_min200.txt'
2.2 分词/词性标注模型
在中文关键词提取中,我采用了ltp4模型进行分词和词性标注工作,对于英文场景,在此我采用的是stanza模型,这是一个基于python实现的,集分词、词性、句法等功能于一身的nlp工具,很方便,无论是安装、修改都比较方便。在这里不进行详细的介绍,如何安装请自行百度。
当然,你也可以利用nltk,stanfordcoreNLP,spacy或其他nlp工具来实现这一功能。
首先我们配置stanza模型的资源目录,用变量stanza_model_path指向这个目录:
stanza_model_path = 'path_to_your_model/model'
然后实现一个类用于分词和词性标注:
class Stanza_for_tokenize_and_postag:
"""
用于英文分词和词性标注
---------------
ver: 2021-11-21
by: changhongyu
"""
def __init__(self, stanza_model_path, use_gpu=True):
"""
:param stanza_model_path:
:param use_gpu:
"""
print("Initializing Stanza model from {}.".format(stanza_model_path))
self.stanza_model = stanza.Pipeline(lang='en', dir=stanza_model_path, use_gpu=use_gpu)
print("Stanza model created.")
def cut(self, text):
"""
:return token_list: list:
:return token_tag_list: list:
"""
token_list = []
token_tag_list = []
doc_res = self.stanza_model(text)
for item in doc_res.sentences:
for word in item.words:
token_list.append(word.text)
token_tag_list.append(word.xpos)
return [[token, tag] for token, tag in zip(token_list, token_tag_list)]
然后把它实例化:
stanza_pos_model = Stanza_for_tokenize_and_postag(stanza_model_path)
这个模型对应的是中文模型中的ltp_pos_model。
值得注意的是,stanza并没有指定gpu_id,只是用来判断是否使用gpu,可以去stanza的源码中对其进行修改,或者通过os或torch.cuda模块对gpu_id进行指定。
2.3 候选短语抽取模型
候选短语抽取与之前大同小异,只是因为stanza对词性的标记规则有所差异,需要对grammar进行配置。
class CandidateExtractor:
"""
参考SIFRank项目的词性正则抽取候选短语
"""
def __init__(self):
grammar = """ NP:
{<NN.*|JJ>*<NN.*>} """
# grammar = """ NP:
# {<n.*|a|uw|i|j|x>*<n.*|uw|x>|<x|j><-><m|q>} # Adjective(s)(optional) + Noun(s)"""
self.parser = nltk.RegexpParser(grammar)
def extract_candidates(self, tokens_tagged):
keyphrase_candidate = []
np_pos_tag_tokens = self.parser.parse(tokens_tagged)
count = 0
for token in np_pos_tag_tokens:
if (isinstance(token, nltk.tree.Tree) and token._label == "NP"):
# 注意英文的空格
# np = ''.join(word for word, tag in token.leaves())
np = ''.join(word + ' ' for word, tag in token.leaves())[: -1]
length = len(token.leaves())
start_end = (count, count + length)
count += length
keyphrase_candidate.append((np, start_end))
else:
count += 1
return keyphrase_candidate
candidate_extractor = CandidateExtractor()
如果希望对grammar进行修改,以按照自己的偏好获取候选关键短语,可以参考我上一篇中文的文章。
另外,这里有一个点需要注意,英文候选关键词中有空格,拼接的时候需要小心,在上面的代码中进行了注释。
2.4 编码模型
编码模型部分没有需要修改的代码,只需去huggingface上下载可以处理英文的预训练模型即可,至于选择哪一个,就看你的喜好了。
最后在**SIFRank(ver: 2021-11-02)**类中的最后部分,为了处理中文,我把空格替换成了特殊字符’▲’,如果处理英文,记得把这行代码注释掉。
本期内容到此结束,希望读到这里的同学有所收获,我们下期再见。