【问题标题】:How to get SHAP values for Huggingface Transformer Model Prediction [Zero-Shot Classification]?如何获得 Huggingface Transformer 模型预测 [零样本分类] 的 SHAP 值?
【发布时间】:2021-12-06 05:48:12
【问题描述】:

通过 Huggingface 给定一个零样本分类任务,如下所示:

from transformers import pipeline
classifier = pipeline("zero-shot-classification", model="facebook/bart-large-mnli")

example_text = "This is an example text about snowflakes in the summer"
labels = ["weather", "sports", "computer industry"]
        
output = classifier(example_text, labels, multi_label=True)
output 
{'sequence': 'This is an example text about snowflakes in the summer',
'labels': ['weather', 'sports'],
'scores': [0.9780895709991455, 0.021910419687628746]}

我正在尝试提取 SHAP 值来为预测结果生成基于文本的解释,如下所示:SHAP for Transformers

我已经根据上面的网址尝试了以下方法:

from transformers import AutoModelForSequenceClassification, AutoTokenizer, ZeroShotClassificationPipeline

model = AutoModelForSequenceClassification.from_pretrained('facebook/bart-large-mnli')
tokenizer = AutoTokenizer.from_pretrained('facebook/bart-large-mnli')

pipe = ZeroShotClassificationPipeline(model=model, tokenizer=tokenizer, return_all_scores=True)

def score_and_visualize(text):
    prediction = pipe([text])
    print(prediction[0])

    explainer = shap.Explainer(pipe)
    shap_values = explainer([text])

    shap.plots.text(shap_values)

score_and_visualize(example_text)

有什么建议吗?提前感谢您的帮助!

除了上述管道之外,以下方法也可以使用:

from transformers import AutoModelForSequenceClassification, AutoTokenizer, ZeroShotClassificationPipeline

model = AutoModelForSequenceClassification.from_pretrained('facebook/bart-large-mnli')
tokenizer = AutoTokenizer.from_pretrained('facebook/bart-large-mnli')

classifier = ZeroShotClassificationPipeline(model=model, tokenizer=tokenizer, return_all_scores=True)

example_text = "This is an example text about snowflakes in the summer"
labels = ["weather", "sports"]

output = classifier(example_text, labels)
output 
{'sequence': 'This is an example text about snowflakes in the summer',
'labels': ['weather', 'sports'],
'scores': [0.9780895709991455, 0.021910419687628746]}

【问题讨论】:

    标签: pytorch huggingface-transformers transformer shap


    【解决方案1】:

    shap 目前不支持ZeroShotClassificationPipeline,但您可以使用解决方法。需要解决方法是因为:

    1. shap Explainer 仅将一个参数转发给模型(在本例中为管道),但 ZeroShotClassificationPipeline 需要两个参数,即文本和标签。
    2. shap Explainer 将访问模型的配置并使用其label2idid2label 属性。它们与 ZeroShotClassificationPipeline 返回的标签不匹配,将导致错误。

    以下是对一种可能的解决方法的建议。我建议在 shap 打开一个问题,并请求官方支持 huggingface 的 ZeroShotClassificationPipeline。

    import shap
    from transformers import AutoModelForSequenceClassification, AutoTokenizer, ZeroShotClassificationPipeline
    from typing import Union, List
    
    weights = "valhalla/distilbart-mnli-12-3"
    
    model = AutoModelForSequenceClassification.from_pretrained(weights)
    tokenizer = AutoTokenizer.from_pretrained(weights)
    
    # Create your own pipeline that only requires the text parameter 
    # for the __call__ method and provides a method to set the labels
    class MyZeroShotClassificationPipeline(ZeroShotClassificationPipeline):
        # Overwrite the __call__ method
        def __call__(self, *args):
          o = super().__call__(args[0], self.workaround_labels)[0]
    
          return [[{"label":x[0], "score": x[1]}  for x in zip(o["labels"], o["scores"])]]
    
        def set_labels_workaround(self, labels: Union[str,List[str]]):
          self.workaround_labels = labels
    
    example_text = "This is an example text about snowflakes in the summer"
    labels = ["weather","sports"]
    
    # In the following, we address issue 2.
    model.config.label2id.update({v:k for k,v in enumerate(labels)})
    model.config.id2label.update({k:v for k,v in enumerate(labels)})
    
    pipe = MyZeroShotClassificationPipeline(model=model, tokenizer=tokenizer, return_all_scores=True)
    pipe.set_labels_workaround(labels)
    
    def score_and_visualize(text):
        prediction = pipe([text])
        print(prediction[0])
    
        explainer = shap.Explainer(pipe)
        shap_values = explainer([text])
    
        shap.plots.text(shap_values)
    
    
    score_and_visualize(example_text)
    

    输出:

    【讨论】:

    • 您正在丢弃原始的{'contradiction': 0, 'entailment': 2, 'neutral': 1} 并用任意所需的标签代替。你能解释一下这在模型级别是如何工作的吗?
    • 底层模型经过训练可以预测 3 个类别。您是说无需重新训练模型即可任意更改标签的数量和含义?
    • 不,你不能。扔掉原来的标签是我的一个复制和粘贴错误。 ZeroShotClassificationPipeline 需要 entailment 标签。我已经更正了我的答案。谢谢你的评论。 @SergeyBushmanov
    • 仍然不是很有说服力。句子/标签对是他们客厅中的premise/hypothesis。完全不清楚是否可以将假设作为预训练的标签传递。
    • ZeroShotClassificationPipeline 创建了premise/hypothesis。它将以下句子"[CLS] This is an example text about snowflakes in the summer" [SEP] This example is sports. [SEP]". 传递给标记化后的模型,并使用entailment logits 进行预测。这就是为什么它被称为零射击。 @SergeyBushmanov
    【解决方案2】:

    这是与@cronoik 讨论的后续内容,这可能有助于其他人理解为什么修补label2id 的魔力会起作用。

    ZeroShotClassificationPipeline 状态的文档:

    使用在 NLI(自然语言推理)任务上训练的 ModelForSequenceClassification 的基于 NLI 的零样本分类管道。

    可以传递序列和标签的任何组合,并且每个组合都将作为前提/假设对并传递给预训练模型。然后,entailment 的 logit 被视为候选标签有效的 logit。可以使用任何 NLI 模型,但 entailment 标签的 id 必须包含在模型配置的 ~transformers.PretrainedConfig.label2id 中。

    这意味着(参见随附的源代码):

    • 通过__call__ 方法提供的标签将被传递到基础训练模型(通过label2id),并将在前提/蕴含句对中进行尝试
    • 如果您手动覆盖label2id,则应将entailment 标签添加到label2id(否则会收到警告)。无需添加任何其他内容。

    一旦满足这些条件,模型将返回所提供标签的字典,分类中的 sigmoid/softmax logits 为 entailment,如

    "<cls> sequence to classify <sep> This example is {label} . <sep>"

    作为label 的蕴涵概率。

    对于这种类型的分类器,管道label2id's 只是用作一个占位符来保存标签并将它们传递给管道的其他部分。

    【讨论】:

      猜你喜欢
      • 2023-01-28
      • 2018-11-03
      • 2021-10-25
      • 2021-12-18
      • 1970-01-01
      • 2021-11-13
      • 1970-01-01
      • 2021-08-29
      • 1970-01-01
      相关资源
      最近更新 更多