如何获得 Huggingface Transformer 模型预测 [零样本分类] 的 SHAP 值?

Posted

技术标签:

【中文标题】如何获得 Huggingface Transformer 模型预测 [零样本分类] 的 SHAP 值?【英文标题】:How to get SHAP values for Huggingface Transformer Model Prediction [Zero-Shot Classification]? 【发布时间】: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]

【问题讨论】:

【参考方案1】:

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

    shap Explainer 仅将一个参数转发给模型(在本例中为管道),但 ZeroShotClassificationPipeline 需要两个参数,即文本和标签。 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 只是用作一个占位符来保存标签并将它们传递给管道的其他部分。

【讨论】:

以上是关于如何获得 Huggingface Transformer 模型预测 [零样本分类] 的 SHAP 值?的主要内容,如果未能解决你的问题,请参考以下文章

在 Huggingface BERT 模型之上添加密集层

如何在 Huggingface 中从 CSV 加载自定义数据集

Huggingface 微调 - 如何在预训练的基础上构建自定义模型

使用 Huggingface TFTrainer 类微调模型时如何指定损失函数?

如何将 HuggingFace 的 Seq2seq 模型转换为 onnx 格式

如何下载 HuggingFace 模型“transformers.trainer.Trainer”?