2020-07-14 18:53:15 +02:00
|
|
|
import math
|
2020-07-10 10:54:56 +02:00
|
|
|
|
2020-10-26 19:19:10 +01:00
|
|
|
import pytest
|
|
|
|
|
2021-10-25 15:50:23 +02:00
|
|
|
from haystack.schema import Document, Answer
|
|
|
|
from haystack.nodes.reader.base import BaseReader
|
|
|
|
from haystack.nodes.reader.farm import FARMReader
|
2020-07-14 18:53:15 +02:00
|
|
|
|
2020-07-10 10:54:56 +02:00
|
|
|
|
|
|
|
def test_reader_basic(reader):
|
|
|
|
assert reader is not None
|
|
|
|
assert isinstance(reader, BaseReader)
|
|
|
|
|
|
|
|
|
2020-07-14 18:53:15 +02:00
|
|
|
def test_output(prediction):
|
|
|
|
assert prediction is not None
|
2020-11-30 17:50:04 +01:00
|
|
|
assert prediction["query"] == "Who lives in Berlin?"
|
2021-10-13 14:23:23 +02:00
|
|
|
assert prediction["answers"][0].answer == "Carla"
|
|
|
|
assert prediction["answers"][0].offsets_in_context[0].start == 11
|
2022-02-03 13:43:18 +01:00
|
|
|
assert prediction["answers"][0].offsets_in_context[0].end == 16
|
2021-10-13 14:23:23 +02:00
|
|
|
assert prediction["answers"][0].score <= 1
|
|
|
|
assert prediction["answers"][0].score >= 0
|
|
|
|
assert prediction["answers"][0].context == "My name is Carla and I live in Berlin"
|
2020-07-14 18:53:15 +02:00
|
|
|
assert len(prediction["answers"]) == 5
|
|
|
|
|
|
|
|
|
2020-10-26 19:19:10 +01:00
|
|
|
@pytest.mark.slow
|
2020-07-14 18:53:15 +02:00
|
|
|
def test_no_answer_output(no_answer_prediction):
|
|
|
|
assert no_answer_prediction is not None
|
2020-11-30 17:50:04 +01:00
|
|
|
assert no_answer_prediction["query"] == "What is the meaning of life?"
|
2020-11-13 10:35:53 +01:00
|
|
|
assert math.isclose(no_answer_prediction["no_ans_gap"], -13.048564434051514, rel_tol=0.0001)
|
2021-10-13 14:23:23 +02:00
|
|
|
assert no_answer_prediction["answers"][0].answer == ""
|
|
|
|
assert no_answer_prediction["answers"][0].offsets_in_context[0].start == 0
|
|
|
|
assert no_answer_prediction["answers"][0].offsets_in_context[0].end == 0
|
|
|
|
assert no_answer_prediction["answers"][0].score <= 1
|
|
|
|
assert no_answer_prediction["answers"][0].score >= 0
|
|
|
|
assert no_answer_prediction["answers"][0].context == None
|
|
|
|
assert no_answer_prediction["answers"][0].document_id == None
|
|
|
|
answers = [x.answer for x in no_answer_prediction["answers"]]
|
|
|
|
assert answers.count("") == 1
|
2020-07-14 18:53:15 +02:00
|
|
|
assert len(no_answer_prediction["answers"]) == 5
|
|
|
|
|
2020-10-26 19:19:10 +01:00
|
|
|
|
2020-07-14 18:53:15 +02:00
|
|
|
# TODO Directly compare farm and transformers reader outputs
|
|
|
|
# TODO checks to see that model is responsive to input arguments e.g. context_window_size - topk
|
|
|
|
|
2020-10-26 19:19:10 +01:00
|
|
|
|
|
|
|
@pytest.mark.slow
|
2020-07-14 18:53:15 +02:00
|
|
|
def test_prediction_attributes(prediction):
|
|
|
|
# TODO FARM's prediction also has no_ans_gap
|
2020-11-30 17:50:04 +01:00
|
|
|
attributes_gold = ["query", "answers"]
|
2020-07-14 18:53:15 +02:00
|
|
|
for ag in attributes_gold:
|
|
|
|
assert ag in prediction
|
|
|
|
|
2022-02-03 13:43:18 +01:00
|
|
|
|
2021-10-18 15:47:36 +02:00
|
|
|
@pytest.mark.slow
|
|
|
|
def test_model_download_options():
|
|
|
|
# download disabled and model is not cached locally
|
|
|
|
with pytest.raises(OSError):
|
2021-12-22 17:20:23 +01:00
|
|
|
impossible_reader = FARMReader("mfeb/albert-xxlarge-v2-squad2", local_files_only=True, num_processes=0)
|
2020-07-14 18:53:15 +02:00
|
|
|
|
2022-02-03 13:43:18 +01:00
|
|
|
|
2020-07-14 18:53:15 +02:00
|
|
|
def test_answer_attributes(prediction):
|
|
|
|
# TODO Transformers answer also has meta key
|
|
|
|
answer = prediction["answers"][0]
|
2021-10-13 14:23:23 +02:00
|
|
|
assert type(answer) == Answer
|
2022-02-03 13:43:18 +01:00
|
|
|
attributes_gold = ["answer", "score", "context", "offsets_in_context", "offsets_in_document", "type"]
|
2020-07-14 18:53:15 +02:00
|
|
|
for ag in attributes_gold:
|
2022-02-03 13:43:18 +01:00
|
|
|
assert getattr(answer, ag, None) is not None
|
2020-07-14 18:53:15 +02:00
|
|
|
|
|
|
|
|
2020-10-26 19:19:10 +01:00
|
|
|
@pytest.mark.slow
|
|
|
|
@pytest.mark.parametrize("reader", ["farm"], indirect=True)
|
|
|
|
@pytest.mark.parametrize("window_size", [10, 15, 20])
|
|
|
|
def test_context_window_size(reader, test_docs_xs, window_size):
|
2020-07-31 11:34:06 +02:00
|
|
|
docs = [Document.from_dict(d) if isinstance(d, dict) else d for d in test_docs_xs]
|
2020-10-26 19:19:10 +01:00
|
|
|
|
2020-10-30 18:06:02 +01:00
|
|
|
assert isinstance(reader, FARMReader)
|
|
|
|
|
|
|
|
old_window_size = reader.inferencer.model.prediction_heads[0].context_window_size
|
|
|
|
reader.inferencer.model.prediction_heads[0].context_window_size = window_size
|
2020-10-26 19:19:10 +01:00
|
|
|
|
2020-11-30 17:50:04 +01:00
|
|
|
prediction = reader.predict(query="Who lives in Berlin?", documents=docs, top_k=5)
|
2020-10-26 19:19:10 +01:00
|
|
|
for answer in prediction["answers"]:
|
|
|
|
# If the extracted answer is larger than the context window, the context window is expanded.
|
|
|
|
# If the extracted answer is odd in length, the resulting context window is one less than context_window_size
|
|
|
|
# due to rounding (See FARM's QACandidate)
|
2020-07-14 18:53:15 +02:00
|
|
|
# TODO Currently the behaviour of context_window_size in FARMReader and TransformerReader is different
|
2021-10-13 14:23:23 +02:00
|
|
|
if len(answer.answer) <= window_size:
|
|
|
|
assert len(answer.context) in [window_size, window_size - 1]
|
2020-10-26 19:19:10 +01:00
|
|
|
else:
|
2021-10-13 14:23:23 +02:00
|
|
|
assert len(answer.answer) == len(answer.context)
|
2020-07-14 18:53:15 +02:00
|
|
|
|
2020-10-30 18:06:02 +01:00
|
|
|
reader.inferencer.model.prediction_heads[0].context_window_size = old_window_size
|
|
|
|
|
2020-10-26 19:19:10 +01:00
|
|
|
# TODO Need to test transformers reader
|
|
|
|
# TODO Currently the behaviour of context_window_size in FARMReader and TransformerReader is different
|
2020-07-14 18:53:15 +02:00
|
|
|
|
2020-07-31 11:34:06 +02:00
|
|
|
|
2020-10-26 19:19:10 +01:00
|
|
|
@pytest.mark.parametrize("reader", ["farm"], indirect=True)
|
|
|
|
@pytest.mark.parametrize("top_k", [2, 5, 10])
|
|
|
|
def test_top_k(reader, test_docs_xs, top_k):
|
2020-07-31 11:34:06 +02:00
|
|
|
docs = [Document.from_dict(d) if isinstance(d, dict) else d for d in test_docs_xs]
|
2020-07-14 18:53:15 +02:00
|
|
|
|
2020-10-30 18:06:02 +01:00
|
|
|
assert isinstance(reader, FARMReader)
|
|
|
|
|
|
|
|
old_top_k_per_candidate = reader.top_k_per_candidate
|
2020-10-26 19:19:10 +01:00
|
|
|
reader.top_k_per_candidate = 4
|
2020-10-30 18:06:02 +01:00
|
|
|
reader.inferencer.model.prediction_heads[0].n_best = reader.top_k_per_candidate + 1
|
|
|
|
try:
|
|
|
|
old_top_k_per_sample = reader.inferencer.model.prediction_heads[0].n_best_per_sample
|
|
|
|
reader.inferencer.model.prediction_heads[0].n_best_per_sample = 4
|
|
|
|
except:
|
|
|
|
print("WARNING: Could not set `top_k_per_sample` in FARM. Please update FARM version.")
|
2020-07-14 18:53:15 +02:00
|
|
|
|
2020-11-30 17:50:04 +01:00
|
|
|
prediction = reader.predict(query="Who lives in Berlin?", documents=docs, top_k=top_k)
|
2020-10-26 19:19:10 +01:00
|
|
|
assert len(prediction["answers"]) == top_k
|
2020-10-30 18:06:02 +01:00
|
|
|
|
|
|
|
reader.top_k_per_candidate = old_top_k_per_candidate
|
|
|
|
reader.inferencer.model.prediction_heads[0].n_best = reader.top_k_per_candidate + 1
|
|
|
|
try:
|
|
|
|
reader.inferencer.model.prediction_heads[0].n_best_per_sample = old_top_k_per_sample
|
|
|
|
except:
|
|
|
|
print("WARNING: Could not set `top_k_per_sample` in FARM. Please update FARM version.")
|
2020-12-07 14:07:20 +01:00
|
|
|
|
|
|
|
|
|
|
|
def test_farm_reader_update_params(test_docs_xs):
|
|
|
|
reader = FARMReader(
|
2022-02-03 13:43:18 +01:00
|
|
|
model_name_or_path="deepset/roberta-base-squad2", use_gpu=False, no_ans_boost=0, num_processes=0
|
2020-12-07 14:07:20 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
docs = [Document.from_dict(d) if isinstance(d, dict) else d for d in test_docs_xs]
|
|
|
|
|
|
|
|
# original reader
|
|
|
|
prediction = reader.predict(query="Who lives in Berlin?", documents=docs, top_k=3)
|
|
|
|
assert len(prediction["answers"]) == 3
|
2021-10-13 14:23:23 +02:00
|
|
|
assert prediction["answers"][0].answer == "Carla"
|
2020-12-07 14:07:20 +01:00
|
|
|
|
|
|
|
# update no_ans_boost
|
|
|
|
reader.update_parameters(
|
|
|
|
context_window_size=100, no_ans_boost=100, return_no_answer=True, max_seq_len=384, doc_stride=128
|
|
|
|
)
|
|
|
|
prediction = reader.predict(query="Who lives in Berlin?", documents=docs, top_k=3)
|
|
|
|
assert len(prediction["answers"]) == 3
|
2021-10-13 14:23:23 +02:00
|
|
|
assert prediction["answers"][0].answer == ""
|
2020-12-07 14:07:20 +01:00
|
|
|
|
|
|
|
# update no_ans_boost
|
|
|
|
reader.update_parameters(
|
|
|
|
context_window_size=100, no_ans_boost=0, return_no_answer=False, max_seq_len=384, doc_stride=128
|
|
|
|
)
|
|
|
|
prediction = reader.predict(query="Who lives in Berlin?", documents=docs, top_k=3)
|
|
|
|
assert len(prediction["answers"]) == 3
|
2021-10-13 14:23:23 +02:00
|
|
|
assert None not in [ans.answer for ans in prediction["answers"]]
|
2020-12-07 14:07:20 +01:00
|
|
|
|
|
|
|
# update context_window_size
|
|
|
|
reader.update_parameters(context_window_size=6, no_ans_boost=-10, max_seq_len=384, doc_stride=128)
|
|
|
|
prediction = reader.predict(query="Who lives in Berlin?", documents=docs, top_k=3)
|
|
|
|
assert len(prediction["answers"]) == 3
|
2021-10-13 14:23:23 +02:00
|
|
|
assert len(prediction["answers"][0].context) == 6
|
2020-12-07 14:07:20 +01:00
|
|
|
|
|
|
|
# update doc_stride with invalid value
|
|
|
|
with pytest.raises(Exception):
|
|
|
|
reader.update_parameters(context_window_size=100, no_ans_boost=-10, max_seq_len=384, doc_stride=999)
|
|
|
|
reader.predict(query="Who lives in Berlin?", documents=docs, top_k=3)
|
|
|
|
|
|
|
|
# update max_seq_len with invalid value
|
|
|
|
with pytest.raises(Exception):
|
|
|
|
reader.update_parameters(context_window_size=6, no_ans_boost=-10, max_seq_len=99, doc_stride=128)
|
|
|
|
reader.predict(query="Who lives in Berlin?", documents=docs, top_k=3)
|