解释一个问答变压器模型

在这里,我们演示如何解释一个问答模型的输出,该模型预测上下文文本的哪个范围包含给定问题的答案。

[1]:
import numpy as np
import torch
import transformers

import shap

# load the model
pmodel = transformers.pipeline("question-answering")
tokenized_qs = None  # variable to store the tokenized data


# define two predictions, one that outputs the logits for the range start,
# and the other for the range end
def f(questions, tokenized_qs, start):
    outs = []
    for q in questions:
        idx = np.argwhere(
            np.array(tokenized_qs["input_ids"]) == pmodel.tokenizer.sep_token_id
        )[0, 0]  # this code assumes that there is only one sentence in data
        d = tokenized_qs.copy()
        d["input_ids"][:idx] = q[:idx]
        d["input_ids"][idx + 1 :] = q[idx + 1 :]
        out = pmodel.model.forward(**{k: torch.tensor(d[k]).reshape(1, -1) for k in d})
        logits = out.start_logits if start else out.end_logits
        outs.append(logits.reshape(-1).detach().numpy())
    return outs


def tokenize_data(data):
    for q in data:
        question, context = q.split("[SEP]")
        tokenized_data = pmodel.tokenizer(question, context)
    return tokenized_data  # this code assumes that there is only one sentence in data


def f_start(questions):
    return f(questions, tokenized_qs, True)


def f_end(questions):
    return f(questions, tokenized_qs, False)


# attach a dynamic output_names property to the models so we can plot the tokens at each output position
def out_names(inputs):
    question, context = inputs.split("[SEP]")
    d = pmodel.tokenizer(question, context)
    return [pmodel.tokenizer.decode([id]) for id in d["input_ids"]]


f_start.output_names = out_names
f_end.output_names = out_names

解释起始位置

这里我们解释模型的起始范围预测。注意,由于模型输出依赖于模型输入的长度,因此我们传递模型的原生分词器进行掩码处理非常重要,这样当我们隐藏文本的部分内容时,我们可以保留相同数量的标记,从而为每个输出位置保留相同的含义。

[2]:
data = [
    "What is on the table?[SEP]When I got home today I saw my cat on the table, and my frog on the floor.",
]  # this code assumes that there is only one sentence in data
tokenized_qs = tokenize_data(data)

explainer_start = shap.Explainer(
    f_start, shap.maskers.Text(tokenizer=pmodel.tokenizer, output_type="ids")
)
shap_values_start = explainer_start(data)

shap.plots.text(shap_values_start)
Partition explainer: 2it [00:32, 32.86s/it]


[0]
outputs
[CLS]
What
is
on
the
table
?
[SEP]
When
I
got
home
today
I
saw
my
cat
on
the
table
,
and
my
frog
on
the
floor
.
[SEP]


-1-4-725-1.26347-1.26347base value-3.97193-3.97193f[CLS](inputs)0.498 What 0.214 on 0.032 home 0.026 got 0.022 the 0.013 today -0.937 ? -0.459 is -0.374 table -0.254 cat -0.25 on -0.233 on the floor -0.182 and -0.137 saw -0.126 , -0.108 . -0.094 my -0.086 frog -0.078 my -0.059 When -0.05 the -0.049 I -0.037 table -0.0 I
inputs
0.0
0.498
What
-0.459
is
0.214
on
0.022
the
-0.374
table
-0.937
?
0.0
[SEP]
-0.059
When
-0.049
I
0.026
got
0.032
home
0.013
today
-0.0
I
-0.137
saw
-0.078
my
-0.254
cat
-0.25
on
-0.05
the
-0.037
table
-0.126
,
-0.182
and
-0.094
my
-0.086
frog
-0.233 / 3
on the floor
-0.108
.
0.0

解释结束位置

这个过程与上面相同,但现在我们解释结束标记。

[3]:
explainer_end = shap.Explainer(f_end, pmodel.tokenizer)
shap_values_end = explainer_end(data)

shap.plots.text(shap_values_end)


[0]
outputs
[CLS]
What
is
on
the
table
?
[SEP]
When
I
got
home
today
I
saw
my
cat
on
the
table
,
and
my
frog
on
the
floor
.
[SEP]


-0-3-636-1.72717-1.72717base value-2.16449-2.16449f[CLS](inputs)0.424 What 0.233 got home 0.231 the 0.215 When I 0.21 today I saw my 0.129 0.069 on -0.635 ? -0.292 cat on -0.221 my frog -0.217 and -0.159 on the floor -0.126 table -0.119 , -0.093 is -0.068 the table -0.018 .
inputs
0.129
0.424
What
-0.093
is
0.069
on
0.231
the
-0.126
table
-0.635
?
0.0
[SEP]
0.215 / 2
When I
0.233 / 2
got home
0.21 / 4
today I saw my
-0.292 / 2
cat on
-0.068 / 2
the table
-0.119
,
-0.217
and
-0.221 / 2
my frog
-0.159 / 3
on the floor
-0.018
.
0.0

解释一个匹配函数

在上面的例子中,我们直接解释了来自模型的输出logits。这要求我们确保只以保持长度的方法扰动输入,以免改变输出logits的含义。一个不那么详细但更灵活的方法是,只需评分模型是否产生了特定的答案。

[4]:
def make_answer_scorer(answers):
    def f(questions):
        out = []
        for q in questions:
            question, context = q.split("[SEP]")
            results = pmodel(question, context, topk=20)
            values = []
            for answer in answers:
                value = 0
                for result in results:
                    if result["answer"] == answer:
                        value = result["score"]
                        break
                values.append(value)
            out.append(values)
        return out

    f.output_names = answers
    return f


f_answers = make_answer_scorer(["my cat", "cat", "my frog"])
explainer_answers = shap.Explainer(f_answers, pmodel.tokenizer)
shap_values_answers = explainer_answers(data)

shap.plots.text(shap_values_answers)


[0]
outputs
my cat
cat
my frog


0.20.10-0.10.30.40.500base value0.4980790.498079fmy cat(inputs)0.14 table 0.097 my 0.056 What 0.049 table 0.049 the 0.042 cat 0.034 on the floor 0.026 on 0.015 saw 0.013 the 0.013 When I got home 0.013 ? 0.008 today I 0.003 , 0.002 on 0.001 0.0 . -0.042 my frog -0.013 is -0.008 and
inputs
0.001
0.056
What
-0.013
is
0.002
on
0.049
the
0.14
table
0.013
?
0.0
[SEP]
0.013 / 4
When I got home
0.008 / 2
today I
0.015
saw
0.097
my
0.042
cat
0.026
on
0.013
the
0.049
table
0.003
,
-0.008
and
-0.042 / 2
my frog
0.034 / 3
on the floor
0.0
.
0.0

有更多有用示例的想法吗?我们鼓励提交增加此文档笔记本的拉取请求!