Presidio中如何构建可动态传入拒绝列表的识别器?
问题描述
加载Presidio分析引擎要花不少时间,我需要给每份文档过滤不同的特定名称,但发现analyze函数只支持传allow_list,没法直接用deny_list。想做一个每次调用能传不同拒绝列表的识别器,还不用每次重新加载分析引擎(避免重复加载NLP模型)。
试了用my_analyzer_first.registry.remove_recognizer方法移除临时添加的识别器,但没效果:跑了代码之后,哪怕调用了移除方法,"Bob"还是被识别成PATIENT,对比移除前后的my_analyzer_first.registry,内容根本没变。
附上测试代码:
from presidio_analyzer import PatternRecognizer, EntityRecognizer, RecognizerResult, AnalyzerEngine, nlp_engine from presidio_analyzer.nlp_engine import NlpArtifacts import time my_analyzer_first = AnalyzerEngine( supported_languages=["en"], default_score_threshold=0.5 ) def on_the_fly_without_loading(text : str, deny_list : list[str]): denylist_recognizer = PatternRecognizer(supported_entity="[PATIENT]", deny_list=deny_list) my_analyzer_first.registry.add_recognizer(denylist_recognizer) entities = my_analyzer_first.analyze(text=text, language="en") my_analyzer_first.registry.remove_recognizer(denylist_recognizer) return entities def sanity_check(text : str): entities = my_analyzer_first.analyze(text=text, language="en") return entities if __name__=="__main__": text = 4 * "I went to the zoo and said hello to Bob the tiger." deny_list = ["Bob"] print(on_the_fly_without_loading(text, deny_list)) print(sanity_check(text))
预期只有第一次调用会过滤"Bob",结果每次调用都过滤了。
问题原因
每次调用on_the_fly_without_loading都会新建一个PatternRecognizer实例,调用remove_recognizer(denylist_recognizer)时,Presidio的注册表没法正确匹配这个新实例(比如实例的唯一标识没被正确识别),导致识别器根本没被移除,后面调用sanity_check时它还在生效。
解决方案
方法1:用识别器名称精准移除
给临时创建的拒绝列表识别器指定一个唯一名称,移除时用名称而非实例对象操作,保证能准确删掉:
修改后的代码:
from presidio_analyzer import PatternRecognizer, AnalyzerEngine my_analyzer_first = AnalyzerEngine( supported_languages=["en"], default_score_threshold=0.5 ) # 定义临时识别器的唯一名称 TEMP_RECOGNIZER_NAME = "temp_denylist_recognizer" def on_the_fly_without_loading(text : str, deny_list : list[str]): # 创建带唯一名称的拒绝列表识别器 denylist_recognizer = PatternRecognizer( supported_entity="PATIENT", deny_list=deny_list, name=TEMP_RECOGNIZER_NAME ) my_analyzer_first.registry.add_recognizer(denylist_recognizer) entities = my_analyzer_first.analyze(text=text, language="en") # 通过名称移除识别器 my_analyzer_first.registry.remove_recognizer(TEMP_RECOGNIZER_NAME) return entities def sanity_check(text : str): entities = my_analyzer_first.analyze(text=text, language="en") return entities if __name__=="__main__": text = 4 * "I went to the zoo and said hello to Bob the tiger." deny_list = ["Bob"] print("带拒绝列表调用结果:", on_the_fly_without_loading(text, deny_list)) print("无拒绝列表调用结果:", sanity_check(text))
方法2:复用单个识别器,动态更新拒绝列表
创建一个全局的拒绝列表识别器,每次调用前更新它的deny_list,不用频繁加删识别器:
from presidio_analyzer import PatternRecognizer, AnalyzerEngine my_analyzer_first = AnalyzerEngine( supported_languages=["en"], default_score_threshold=0.5 ) # 创建可复用的拒绝列表识别器,初始为空 denylist_recognizer = PatternRecognizer( supported_entity="PATIENT", deny_list=[], name="reusable_denylist_recognizer" ) # 一次性添加到注册表 my_analyzer_first.registry.add_recognizer(denylist_recognizer) def on_the_fly_without_loading(text : str, deny_list : list[str]): # 动态更新拒绝列表 denylist_recognizer.deny_list = deny_list entities = my_analyzer_first.analyze(text=text, language="en") # 可选:调用后清空,避免影响后续无拒绝列表的调用 denylist_recognizer.deny_list = [] return entities def sanity_check(text : str): entities = my_analyzer_first.analyze(text=text, language="en") return entities if __name__=="__main__": text = 4 * "I went to the zoo and said hello to Bob the tiger." deny_list = ["Bob"] print("带拒绝列表调用结果:", on_the_fly_without_loading(text, deny_list)) print("无拒绝列表调用结果:", sanity_check(text))
方法3:识别后手动过滤结果
如果不想折腾注册表,也可以拿到识别结果后,手动把拒绝列表里的实体删掉:
from presidio_analyzer import AnalyzerEngine my_analyzer_first = AnalyzerEngine( supported_languages=["en"], default_score_threshold=0.5 ) def on_the_fly_without_loading(text : str, deny_list : list[str]): entities = my_analyzer_first.analyze(text=text, language="en") # 过滤掉拒绝列表中的实体 filtered_entities = [ ent for ent in entities if text[ent.start:ent.end] not in deny_list ] return filtered_entities def sanity_check(text : str): entities = my_analyzer_first.analyze(text=text, language="en") return entities if __name__=="__main__": text = 4 * "I went to the zoo and said hello to Bob the tiger." deny_list = ["Bob"] print("带拒绝列表调用结果:", on_the_fly_without_loading(text, deny_list)) print("无拒绝列表调用结果:", sanity_check(text))
内容的提问来源于stack exchange,提问作者Pink Flying Elephant
相关产品推荐
相关产品推荐

