AutoRAG 0.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- autorag/__init__.py +82 -0
- autorag/chunker.py +51 -0
- autorag/cli.py +209 -0
- autorag/dashboard.py +199 -0
- autorag/data/__init__.py +109 -0
- autorag/data/chunk/__init__.py +2 -0
- autorag/data/chunk/base.py +128 -0
- autorag/data/chunk/langchain_chunk.py +76 -0
- autorag/data/chunk/llama_index_chunk.py +96 -0
- autorag/data/chunk/run.py +38 -0
- autorag/data/legacy/__init__.py +0 -0
- autorag/data/legacy/corpus/__init__.py +2 -0
- autorag/data/legacy/corpus/langchain.py +47 -0
- autorag/data/legacy/corpus/llama_index.py +93 -0
- autorag/data/legacy/qacreation/__init__.py +6 -0
- autorag/data/legacy/qacreation/base.py +239 -0
- autorag/data/legacy/qacreation/llama_index.py +253 -0
- autorag/data/legacy/qacreation/llama_index_default_prompt.txt +54 -0
- autorag/data/legacy/qacreation/ragas.py +75 -0
- autorag/data/legacy/qacreation/simple.py +99 -0
- autorag/data/parse/__init__.py +1 -0
- autorag/data/parse/base.py +79 -0
- autorag/data/parse/clova.py +194 -0
- autorag/data/parse/langchain_parse.py +87 -0
- autorag/data/parse/llamaparse.py +126 -0
- autorag/data/parse/run.py +141 -0
- autorag/data/parse/table_hybrid_parse.py +134 -0
- autorag/data/qa/__init__.py +3 -0
- autorag/data/qa/evolve/__init__.py +0 -0
- autorag/data/qa/evolve/llama_index_query_evolve.py +64 -0
- autorag/data/qa/evolve/openai_query_evolve.py +81 -0
- autorag/data/qa/evolve/prompt.py +288 -0
- autorag/data/qa/extract_evidence.py +1 -0
- autorag/data/qa/filter/__init__.py +0 -0
- autorag/data/qa/filter/dontknow.py +117 -0
- autorag/data/qa/filter/passage_dependency.py +88 -0
- autorag/data/qa/filter/prompt.py +73 -0
- autorag/data/qa/generation_gt/__init__.py +0 -0
- autorag/data/qa/generation_gt/base.py +16 -0
- autorag/data/qa/generation_gt/llama_index_gen_gt.py +41 -0
- autorag/data/qa/generation_gt/openai_gen_gt.py +84 -0
- autorag/data/qa/generation_gt/prompt.py +27 -0
- autorag/data/qa/query/__init__.py +0 -0
- autorag/data/qa/query/llama_gen_query.py +82 -0
- autorag/data/qa/query/openai_gen_query.py +95 -0
- autorag/data/qa/query/prompt.py +201 -0
- autorag/data/qa/sample.py +26 -0
- autorag/data/qa/schema.py +322 -0
- autorag/data/utils/__init__.py +0 -0
- autorag/data/utils/util.py +103 -0
- autorag/deploy/__init__.py +9 -0
- autorag/deploy/api.py +303 -0
- autorag/deploy/base.py +235 -0
- autorag/deploy/gradio.py +74 -0
- autorag/deploy/swagger.yml +202 -0
- autorag/embedding/__init__.py +0 -0
- autorag/embedding/base.py +144 -0
- autorag/embedding/vllm.py +256 -0
- autorag/evaluation/__init__.py +3 -0
- autorag/evaluation/generation.py +88 -0
- autorag/evaluation/metric/__init__.py +22 -0
- autorag/evaluation/metric/deepeval_prompt.py +322 -0
- autorag/evaluation/metric/g_eval_prompts/coh_detailed.txt +32 -0
- autorag/evaluation/metric/g_eval_prompts/con_detailed.txt +33 -0
- autorag/evaluation/metric/g_eval_prompts/flu_detailed.txt +26 -0
- autorag/evaluation/metric/g_eval_prompts/rel_detailed.txt +33 -0
- autorag/evaluation/metric/generation.py +504 -0
- autorag/evaluation/metric/retrieval.py +115 -0
- autorag/evaluation/metric/retrieval_contents.py +65 -0
- autorag/evaluation/metric/util.py +88 -0
- autorag/evaluation/retrieval.py +83 -0
- autorag/evaluation/retrieval_contents.py +65 -0
- autorag/evaluation/util.py +43 -0
- autorag/evaluator.py +559 -0
- autorag/node_line.py +65 -0
- autorag/nodes/__init__.py +0 -0
- autorag/nodes/generator/__init__.py +4 -0
- autorag/nodes/generator/base.py +103 -0
- autorag/nodes/generator/llama_index_llm.py +169 -0
- autorag/nodes/generator/openai_llm.py +329 -0
- autorag/nodes/generator/run.py +148 -0
- autorag/nodes/generator/vllm.py +147 -0
- autorag/nodes/generator/vllm_api.py +191 -0
- autorag/nodes/hybridretrieval/__init__.py +2 -0
- autorag/nodes/hybridretrieval/base.py +58 -0
- autorag/nodes/hybridretrieval/hybrid_cc.py +227 -0
- autorag/nodes/hybridretrieval/hybrid_rrf.py +149 -0
- autorag/nodes/hybridretrieval/run.py +137 -0
- autorag/nodes/lexicalretrieval/__init__.py +1 -0
- autorag/nodes/lexicalretrieval/bm25.py +381 -0
- autorag/nodes/lexicalretrieval/run.py +148 -0
- autorag/nodes/passageaugmenter/__init__.py +2 -0
- autorag/nodes/passageaugmenter/base.py +76 -0
- autorag/nodes/passageaugmenter/pass_passage_augmenter.py +43 -0
- autorag/nodes/passageaugmenter/prev_next_augmenter.py +155 -0
- autorag/nodes/passageaugmenter/run.py +131 -0
- autorag/nodes/passagecompressor/__init__.py +4 -0
- autorag/nodes/passagecompressor/base.py +78 -0
- autorag/nodes/passagecompressor/longllmlingua.py +115 -0
- autorag/nodes/passagecompressor/pass_compressor.py +16 -0
- autorag/nodes/passagecompressor/refine.py +54 -0
- autorag/nodes/passagecompressor/run.py +186 -0
- autorag/nodes/passagecompressor/tree_summarize.py +56 -0
- autorag/nodes/passagefilter/__init__.py +6 -0
- autorag/nodes/passagefilter/base.py +40 -0
- autorag/nodes/passagefilter/pass_passage_filter.py +14 -0
- autorag/nodes/passagefilter/percentile_cutoff.py +58 -0
- autorag/nodes/passagefilter/recency.py +105 -0
- autorag/nodes/passagefilter/run.py +138 -0
- autorag/nodes/passagefilter/similarity_percentile_cutoff.py +134 -0
- autorag/nodes/passagefilter/similarity_threshold_cutoff.py +112 -0
- autorag/nodes/passagefilter/threshold_cutoff.py +78 -0
- autorag/nodes/passagereranker/__init__.py +16 -0
- autorag/nodes/passagereranker/base.py +44 -0
- autorag/nodes/passagereranker/cohere.py +118 -0
- autorag/nodes/passagereranker/colbert.py +213 -0
- autorag/nodes/passagereranker/flag_embedding.py +112 -0
- autorag/nodes/passagereranker/flag_embedding_llm.py +101 -0
- autorag/nodes/passagereranker/flashrank.py +245 -0
- autorag/nodes/passagereranker/jina.py +115 -0
- autorag/nodes/passagereranker/koreranker.py +136 -0
- autorag/nodes/passagereranker/mixedbreadai.py +126 -0
- autorag/nodes/passagereranker/monot5.py +190 -0
- autorag/nodes/passagereranker/openvino.py +191 -0
- autorag/nodes/passagereranker/pass_reranker.py +31 -0
- autorag/nodes/passagereranker/rankgpt.py +170 -0
- autorag/nodes/passagereranker/run.py +145 -0
- autorag/nodes/passagereranker/sentence_transformer.py +129 -0
- autorag/nodes/passagereranker/tart/__init__.py +1 -0
- autorag/nodes/passagereranker/tart/modeling_enc_t5.py +152 -0
- autorag/nodes/passagereranker/tart/tart.py +139 -0
- autorag/nodes/passagereranker/tart/tokenization_enc_t5.py +112 -0
- autorag/nodes/passagereranker/time_reranker.py +72 -0
- autorag/nodes/passagereranker/upr.py +160 -0
- autorag/nodes/passagereranker/voyageai.py +109 -0
- autorag/nodes/promptmaker/__init__.py +12 -0
- autorag/nodes/promptmaker/base.py +32 -0
- autorag/nodes/promptmaker/chat_fstring.py +73 -0
- autorag/nodes/promptmaker/fstring.py +49 -0
- autorag/nodes/promptmaker/long_context_reorder.py +83 -0
- autorag/nodes/promptmaker/run.py +283 -0
- autorag/nodes/promptmaker/window_replacement.py +85 -0
- autorag/nodes/queryexpansion/__init__.py +4 -0
- autorag/nodes/queryexpansion/base.py +62 -0
- autorag/nodes/queryexpansion/hyde.py +43 -0
- autorag/nodes/queryexpansion/multi_query_expansion.py +57 -0
- autorag/nodes/queryexpansion/pass_query_expansion.py +22 -0
- autorag/nodes/queryexpansion/query_decompose.py +111 -0
- autorag/nodes/queryexpansion/run.py +308 -0
- autorag/nodes/retrieval/__init__.py +0 -0
- autorag/nodes/retrieval/base.py +127 -0
- autorag/nodes/retrieval/run_util.py +152 -0
- autorag/nodes/semanticretrieval/__init__.py +1 -0
- autorag/nodes/semanticretrieval/run.py +148 -0
- autorag/nodes/semanticretrieval/vectordb.py +339 -0
- autorag/nodes/util.py +16 -0
- autorag/parser.py +37 -0
- autorag/schema/__init__.py +3 -0
- autorag/schema/base.py +35 -0
- autorag/schema/metricinput.py +99 -0
- autorag/schema/module.py +24 -0
- autorag/schema/node.py +144 -0
- autorag/strategy.py +165 -0
- autorag/support.py +235 -0
- autorag/utils/__init__.py +8 -0
- autorag/utils/cast.py +45 -0
- autorag/utils/preprocess.py +149 -0
- autorag/utils/util.py +759 -0
- autorag/validator.py +98 -0
- autorag/vectordb/__init__.py +75 -0
- autorag/vectordb/base.py +73 -0
- autorag/vectordb/chroma.py +118 -0
- autorag/vectordb/couchbase.py +239 -0
- autorag/vectordb/milvus.py +169 -0
- autorag/vectordb/pinecone.py +121 -0
- autorag/vectordb/qdrant.py +155 -0
- autorag/vectordb/weaviate.py +184 -0
- autorag/web.py +81 -0
- autorag-0.0.0.dist-info/METADATA +780 -0
- autorag-0.0.0.dist-info/RECORD +184 -0
- autorag-0.0.0.dist-info/WHEEL +5 -0
- autorag-0.0.0.dist-info/entry_points.txt +2 -0
- autorag-0.0.0.dist-info/licenses/LICENSE +201 -0
- autorag-0.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
|
2
|
+
|
|
3
|
+
import copy
|
|
4
|
+
|
|
5
|
+
from transformers.modeling_outputs import SequenceClassifierOutput
|
|
6
|
+
from transformers.models.t5.modeling_t5 import T5Config, T5PreTrainedModel, T5Stack
|
|
7
|
+
from transformers.utils.model_parallel_utils import assert_device_map, get_device_map
|
|
8
|
+
|
|
9
|
+
from autorag.utils.util import empty_cuda_cache
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class EncT5ForSequenceClassification(T5PreTrainedModel):
|
|
13
|
+
_keys_to_ignore_on_load_missing = [
|
|
14
|
+
r"encoder\.embed_tokens\.weight",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
def __init__(self, config: T5Config, dropout=0.1):
|
|
18
|
+
super().__init__(config)
|
|
19
|
+
try:
|
|
20
|
+
from torch import nn
|
|
21
|
+
except ImportError:
|
|
22
|
+
raise ImportError("Please install PyTorch to use TART reranker.")
|
|
23
|
+
self.num_labels = config.num_labels
|
|
24
|
+
self.config = config
|
|
25
|
+
|
|
26
|
+
self.shared = nn.Embedding(config.vocab_size, config.d_model)
|
|
27
|
+
|
|
28
|
+
encoder_config = copy.deepcopy(config)
|
|
29
|
+
encoder_config.use_cache = False
|
|
30
|
+
encoder_config.is_encoder_decoder = False
|
|
31
|
+
self.encoder = T5Stack(encoder_config, self.shared)
|
|
32
|
+
|
|
33
|
+
self.dropout = nn.Dropout(dropout)
|
|
34
|
+
self.classifier = nn.Linear(config.hidden_size, config.num_labels)
|
|
35
|
+
|
|
36
|
+
# Initialize weights and apply final processing
|
|
37
|
+
self.post_init()
|
|
38
|
+
|
|
39
|
+
# Model parallel
|
|
40
|
+
self.model_parallel = False
|
|
41
|
+
self.device_map = None
|
|
42
|
+
|
|
43
|
+
def parallelize(self, device_map=None):
|
|
44
|
+
try:
|
|
45
|
+
import torch
|
|
46
|
+
except ImportError:
|
|
47
|
+
raise ImportError("Please install PyTorch to use TART reranker.")
|
|
48
|
+
self.device_map = (
|
|
49
|
+
get_device_map(len(self.encoder.block), range(torch.cuda.device_count()))
|
|
50
|
+
if device_map is None
|
|
51
|
+
else device_map
|
|
52
|
+
)
|
|
53
|
+
assert_device_map(self.device_map, len(self.encoder.block))
|
|
54
|
+
self.encoder.parallelize(self.device_map)
|
|
55
|
+
self.classifier = self.classifier.to(self.encoder.first_device)
|
|
56
|
+
self.model_parallel = True
|
|
57
|
+
|
|
58
|
+
def deparallelize(self):
|
|
59
|
+
self.encoder.deparallelize()
|
|
60
|
+
self.encoder = self.encoder.to("cpu")
|
|
61
|
+
self.model_parallel = False
|
|
62
|
+
self.device_map = None
|
|
63
|
+
empty_cuda_cache()
|
|
64
|
+
|
|
65
|
+
def get_input_embeddings(self):
|
|
66
|
+
return self.shared
|
|
67
|
+
|
|
68
|
+
def set_input_embeddings(self, new_embeddings):
|
|
69
|
+
self.shared = new_embeddings
|
|
70
|
+
self.encoder.set_input_embeddings(new_embeddings)
|
|
71
|
+
|
|
72
|
+
def get_encoder(self):
|
|
73
|
+
return self.encoder
|
|
74
|
+
|
|
75
|
+
def _prune_heads(self, heads_to_prune):
|
|
76
|
+
"""
|
|
77
|
+
Prunes heads of the model. heads_to_prune: dict of {layer_num: list of heads to prune in this layer} See base
|
|
78
|
+
class PreTrainedModel
|
|
79
|
+
"""
|
|
80
|
+
for layer, heads in heads_to_prune.items():
|
|
81
|
+
self.encoder.layer[layer].attention.prune_heads(heads)
|
|
82
|
+
|
|
83
|
+
def forward(
|
|
84
|
+
self,
|
|
85
|
+
input_ids=None,
|
|
86
|
+
attention_mask=None,
|
|
87
|
+
head_mask=None,
|
|
88
|
+
inputs_embeds=None,
|
|
89
|
+
labels=None,
|
|
90
|
+
output_attentions=None,
|
|
91
|
+
output_hidden_states=None,
|
|
92
|
+
return_dict=None,
|
|
93
|
+
):
|
|
94
|
+
try:
|
|
95
|
+
import torch
|
|
96
|
+
from torch.nn import BCEWithLogitsLoss, CrossEntropyLoss, MSELoss
|
|
97
|
+
except ImportError:
|
|
98
|
+
raise ImportError("Please install PyTorch to use TART reranker.")
|
|
99
|
+
return_dict = (
|
|
100
|
+
return_dict if return_dict is not None else self.config.use_return_dict
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
outputs = self.encoder(
|
|
104
|
+
input_ids=input_ids,
|
|
105
|
+
attention_mask=attention_mask,
|
|
106
|
+
inputs_embeds=inputs_embeds,
|
|
107
|
+
head_mask=head_mask,
|
|
108
|
+
output_attentions=output_attentions,
|
|
109
|
+
output_hidden_states=output_hidden_states,
|
|
110
|
+
return_dict=return_dict,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
hidden_states = outputs[0]
|
|
114
|
+
pooled_output = hidden_states[:, 0, :] # Take bos token (equiv. to <s>)
|
|
115
|
+
|
|
116
|
+
pooled_output = self.dropout(pooled_output)
|
|
117
|
+
logits = self.classifier(pooled_output)
|
|
118
|
+
|
|
119
|
+
loss = None
|
|
120
|
+
if labels is not None:
|
|
121
|
+
if self.config.problem_type is None:
|
|
122
|
+
if self.num_labels == 1:
|
|
123
|
+
self.config.problem_type = "regression"
|
|
124
|
+
elif self.num_labels > 1 and (
|
|
125
|
+
labels.dtype == torch.long or labels.dtype == torch.int
|
|
126
|
+
):
|
|
127
|
+
self.config.problem_type = "single_label_classification"
|
|
128
|
+
else:
|
|
129
|
+
self.config.problem_type = "multi_label_classification"
|
|
130
|
+
|
|
131
|
+
if self.config.problem_type == "regression":
|
|
132
|
+
loss_fct = MSELoss()
|
|
133
|
+
if self.num_labels == 1:
|
|
134
|
+
loss = loss_fct(logits.squeeze(), labels.squeeze())
|
|
135
|
+
else:
|
|
136
|
+
loss = loss_fct(logits, labels)
|
|
137
|
+
elif self.config.problem_type == "single_label_classification":
|
|
138
|
+
loss_fct = CrossEntropyLoss()
|
|
139
|
+
loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1))
|
|
140
|
+
elif self.config.problem_type == "multi_label_classification":
|
|
141
|
+
loss_fct = BCEWithLogitsLoss()
|
|
142
|
+
loss = loss_fct(logits, labels)
|
|
143
|
+
if not return_dict:
|
|
144
|
+
output = (logits,) + outputs[1:]
|
|
145
|
+
return ((loss,) + output) if loss is not None else output
|
|
146
|
+
|
|
147
|
+
return SequenceClassifierOutput(
|
|
148
|
+
loss=loss,
|
|
149
|
+
logits=logits,
|
|
150
|
+
hidden_states=outputs.hidden_states,
|
|
151
|
+
attentions=outputs.attentions,
|
|
152
|
+
)
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
from itertools import chain
|
|
2
|
+
from typing import List, Tuple
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
from autorag.nodes.passagereranker.base import BasePassageReranker
|
|
7
|
+
from autorag.nodes.passagereranker.tart.modeling_enc_t5 import (
|
|
8
|
+
EncT5ForSequenceClassification,
|
|
9
|
+
)
|
|
10
|
+
from autorag.nodes.passagereranker.tart.tokenization_enc_t5 import EncT5Tokenizer
|
|
11
|
+
from autorag.utils.util import (
|
|
12
|
+
make_batch,
|
|
13
|
+
sort_by_scores,
|
|
14
|
+
flatten_apply,
|
|
15
|
+
select_top_k,
|
|
16
|
+
result_to_dataframe,
|
|
17
|
+
empty_cuda_cache,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Tart(BasePassageReranker):
|
|
22
|
+
def __init__(self, project_dir: str, *args, **kwargs):
|
|
23
|
+
super().__init__(project_dir)
|
|
24
|
+
try:
|
|
25
|
+
import torch
|
|
26
|
+
except ImportError:
|
|
27
|
+
raise ImportError(
|
|
28
|
+
"torch is not installed. Please install torch first to use TART reranker."
|
|
29
|
+
)
|
|
30
|
+
model_name = "facebook/tart-full-flan-t5-xl"
|
|
31
|
+
self.model = EncT5ForSequenceClassification.from_pretrained(model_name)
|
|
32
|
+
self.tokenizer = EncT5Tokenizer.from_pretrained(model_name)
|
|
33
|
+
self.device = "cuda" if torch.cuda.is_available() else "cpu"
|
|
34
|
+
self.model = self.model.to(self.device)
|
|
35
|
+
|
|
36
|
+
def __del__(self):
|
|
37
|
+
del self.model
|
|
38
|
+
del self.tokenizer
|
|
39
|
+
empty_cuda_cache()
|
|
40
|
+
super().__del__()
|
|
41
|
+
|
|
42
|
+
@result_to_dataframe(["retrieved_contents", "retrieved_ids", "retrieve_scores"])
|
|
43
|
+
def pure(self, previous_result: pd.DataFrame, *args, **kwargs):
|
|
44
|
+
queries, contents, _, ids = self.cast_to_run(previous_result)
|
|
45
|
+
top_k = kwargs.pop("top_k")
|
|
46
|
+
instruction = kwargs.pop("instruction", "Find passage to answer given question")
|
|
47
|
+
batch = kwargs.pop("batch", 64)
|
|
48
|
+
return self._pure(queries, contents, ids, top_k, instruction, batch)
|
|
49
|
+
|
|
50
|
+
def _pure(
|
|
51
|
+
self,
|
|
52
|
+
queries: List[str],
|
|
53
|
+
contents_list: List[List[str]],
|
|
54
|
+
ids_list: List[List[str]],
|
|
55
|
+
top_k: int,
|
|
56
|
+
instruction: str = "Find passage to answer given question",
|
|
57
|
+
batch: int = 64,
|
|
58
|
+
) -> Tuple[List[List[str]], List[List[str]], List[List[float]]]:
|
|
59
|
+
"""
|
|
60
|
+
Rerank a list of contents based on their relevance to a query using Tart.
|
|
61
|
+
TART is a reranker based on TART (https://github.com/facebookresearch/tart).
|
|
62
|
+
You can rerank the passages with the instruction using TARTReranker.
|
|
63
|
+
The default model is facebook/tart-full-flan-t5-xl.
|
|
64
|
+
|
|
65
|
+
:param queries: The list of queries to use for reranking
|
|
66
|
+
:param contents_list: The list of lists of contents to rerank
|
|
67
|
+
:param ids_list: The list of lists of ids retrieved from the initial ranking
|
|
68
|
+
:param top_k: The number of passages to be retrieved
|
|
69
|
+
:param instruction: The instruction for reranking.
|
|
70
|
+
Note: default instruction is "Find passage to answer given question"
|
|
71
|
+
The default instruction from the TART paper is being used.
|
|
72
|
+
If you want to use a different instruction, you can change the instruction through this parameter
|
|
73
|
+
:param batch: The number of queries to be processed in a batch
|
|
74
|
+
:return: tuple of lists containing the reranked contents, ids, and scores
|
|
75
|
+
"""
|
|
76
|
+
nested_list = [
|
|
77
|
+
[["{} [SEP] {}".format(instruction, query)] for _ in contents]
|
|
78
|
+
for query, contents in zip(queries, contents_list)
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
rerank_scores = flatten_apply(
|
|
82
|
+
tart_run_model,
|
|
83
|
+
nested_list,
|
|
84
|
+
model=self.model,
|
|
85
|
+
batch_size=batch,
|
|
86
|
+
tokenizer=self.tokenizer,
|
|
87
|
+
device=self.device,
|
|
88
|
+
contents_list=contents_list,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
df = pd.DataFrame(
|
|
92
|
+
{
|
|
93
|
+
"contents": contents_list,
|
|
94
|
+
"ids": ids_list,
|
|
95
|
+
"scores": rerank_scores,
|
|
96
|
+
}
|
|
97
|
+
)
|
|
98
|
+
df[["contents", "ids", "scores"]] = df.apply(
|
|
99
|
+
sort_by_scores, axis=1, result_type="expand"
|
|
100
|
+
)
|
|
101
|
+
results = select_top_k(df, ["contents", "ids", "scores"], top_k)
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
results["contents"].tolist(),
|
|
105
|
+
results["ids"].tolist(),
|
|
106
|
+
results["scores"].tolist(),
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def tart_run_model(
|
|
111
|
+
input_texts, contents_list, model, batch_size: int, tokenizer, device
|
|
112
|
+
):
|
|
113
|
+
try:
|
|
114
|
+
import torch
|
|
115
|
+
import torch.nn.functional as F
|
|
116
|
+
except ImportError:
|
|
117
|
+
raise ImportError(
|
|
118
|
+
"torch is not installed. Please install torch first to use TART reranker."
|
|
119
|
+
)
|
|
120
|
+
flattened_texts = list(chain.from_iterable(input_texts))
|
|
121
|
+
flattened_contents = list(chain.from_iterable(contents_list))
|
|
122
|
+
batch_input_texts = make_batch(flattened_texts, batch_size)
|
|
123
|
+
batch_contents_list = make_batch(flattened_contents, batch_size)
|
|
124
|
+
results = []
|
|
125
|
+
for batch_texts, batch_contents in zip(batch_input_texts, batch_contents_list):
|
|
126
|
+
feature = tokenizer(
|
|
127
|
+
batch_texts,
|
|
128
|
+
batch_contents,
|
|
129
|
+
padding=True,
|
|
130
|
+
truncation=True,
|
|
131
|
+
return_tensors="pt",
|
|
132
|
+
).to(device)
|
|
133
|
+
with torch.no_grad():
|
|
134
|
+
pred_scores = model(**feature).logits
|
|
135
|
+
normalized_scores = [
|
|
136
|
+
float(score[1]) for score in F.softmax(pred_scores, dim=1)
|
|
137
|
+
]
|
|
138
|
+
results.extend(normalized_scores)
|
|
139
|
+
return results
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
from transformers import T5Tokenizer
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class EncT5Tokenizer(T5Tokenizer):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
vocab_file,
|
|
12
|
+
bos_token="<s>",
|
|
13
|
+
eos_token="</s>",
|
|
14
|
+
unk_token="<unk>",
|
|
15
|
+
pad_token="<pad>",
|
|
16
|
+
extra_ids=100,
|
|
17
|
+
additional_special_tokens=None,
|
|
18
|
+
sp_model_kwargs: Optional[Dict[str, Any]] = None,
|
|
19
|
+
**kwargs,
|
|
20
|
+
) -> None:
|
|
21
|
+
sp_model_kwargs = {} if sp_model_kwargs is None else sp_model_kwargs
|
|
22
|
+
|
|
23
|
+
super().__init__(
|
|
24
|
+
vocab_file=vocab_file,
|
|
25
|
+
bos_token=bos_token,
|
|
26
|
+
eos_token=eos_token,
|
|
27
|
+
unk_token=unk_token,
|
|
28
|
+
pad_token=pad_token,
|
|
29
|
+
extra_ids=extra_ids,
|
|
30
|
+
additional_special_tokens=additional_special_tokens,
|
|
31
|
+
sp_model_kwargs=sp_model_kwargs,
|
|
32
|
+
**kwargs,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
def get_special_tokens_mask(
|
|
36
|
+
self,
|
|
37
|
+
token_ids_0: List[int],
|
|
38
|
+
token_ids_1: Optional[List[int]] = None,
|
|
39
|
+
already_has_special_tokens: bool = False,
|
|
40
|
+
) -> List[int]:
|
|
41
|
+
"""
|
|
42
|
+
Retrieve sequence ids from a token list that has no special tokens added. This method is called when adding
|
|
43
|
+
special tokens using the tokenizer `prepare_for_model` method.
|
|
44
|
+
Args:
|
|
45
|
+
token_ids_0 (`List[int]`):
|
|
46
|
+
List of IDs.
|
|
47
|
+
token_ids_1 (`List[int]`, *optional*):
|
|
48
|
+
Optional second list of IDs for sequence pairs.
|
|
49
|
+
already_has_special_tokens (`bool`, *optional*, defaults to `False`):
|
|
50
|
+
Whether or not the token list is already formatted with special tokens for the model.
|
|
51
|
+
Returns:
|
|
52
|
+
`List[int]`: A list of integers in the range [0, 1]: 1 for a special token, 0 for a sequence token.
|
|
53
|
+
"""
|
|
54
|
+
if already_has_special_tokens:
|
|
55
|
+
return super().get_special_tokens_mask(
|
|
56
|
+
token_ids_0=token_ids_0,
|
|
57
|
+
token_ids_1=token_ids_1,
|
|
58
|
+
already_has_special_tokens=True,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# normal case: some special tokens
|
|
62
|
+
if token_ids_1 is None:
|
|
63
|
+
return [1] + ([0] * len(token_ids_0)) + [1]
|
|
64
|
+
return [1] + ([0] * len(token_ids_0)) + [1] + ([0] * len(token_ids_1)) + [1]
|
|
65
|
+
|
|
66
|
+
def create_token_type_ids_from_sequences(
|
|
67
|
+
self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None
|
|
68
|
+
) -> List[int]:
|
|
69
|
+
"""
|
|
70
|
+
Create a mask from the two sequences passed to be used in a sequence-pair classification task. T5 does not make
|
|
71
|
+
use of token type ids, therefore a list of zeros is returned.
|
|
72
|
+
Args:
|
|
73
|
+
token_ids_0 (`List[int]`):
|
|
74
|
+
List of IDs.
|
|
75
|
+
token_ids_1 (`List[int]`, *optional*):
|
|
76
|
+
Optional second list of IDs for sequence pairs.
|
|
77
|
+
Returns:
|
|
78
|
+
`List[int]`: List of zeros.
|
|
79
|
+
"""
|
|
80
|
+
bos = [self.bos_token_id]
|
|
81
|
+
eos = [self.eos_token_id]
|
|
82
|
+
|
|
83
|
+
if token_ids_1 is None:
|
|
84
|
+
return len(bos + token_ids_0 + eos) * [0]
|
|
85
|
+
return len(bos + token_ids_0 + eos + token_ids_1 + eos) * [0]
|
|
86
|
+
|
|
87
|
+
def build_inputs_with_special_tokens(
|
|
88
|
+
self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None
|
|
89
|
+
) -> List[int]:
|
|
90
|
+
"""
|
|
91
|
+
Build model inputs from a sequence or a pair of sequence for sequence classification tasks by concatenating and
|
|
92
|
+
adding special tokens. A sequence has the following format:
|
|
93
|
+
- single sequence: `<s> X </s>`
|
|
94
|
+
- pair of sequences: `<s> A </s> B </s>`
|
|
95
|
+
Args:
|
|
96
|
+
token_ids_0 (`List[int]`):
|
|
97
|
+
List of IDs to which the special tokens will be added.
|
|
98
|
+
token_ids_1 (`List[int]`, *optional*):
|
|
99
|
+
Optional second list of IDs for sequence pairs.
|
|
100
|
+
Returns:
|
|
101
|
+
`List[int]`: List of [input IDs](../glossary#input-ids) with the appropriate special tokens.
|
|
102
|
+
"""
|
|
103
|
+
if token_ids_1 is None:
|
|
104
|
+
return [self.bos_token_id] + token_ids_0 + [self.eos_token_id]
|
|
105
|
+
else:
|
|
106
|
+
return (
|
|
107
|
+
[self.bos_token_id]
|
|
108
|
+
+ token_ids_0
|
|
109
|
+
+ [self.eos_token_id]
|
|
110
|
+
+ token_ids_1
|
|
111
|
+
+ [self.eos_token_id]
|
|
112
|
+
)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from typing import List, Tuple
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
from autorag.nodes.passagereranker.base import BasePassageReranker
|
|
8
|
+
from autorag.utils import result_to_dataframe, fetch_contents
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TimeReranker(BasePassageReranker):
|
|
12
|
+
def __init__(self, project_dir: str, *args, **kwargs):
|
|
13
|
+
super().__init__(project_dir, *args, **kwargs)
|
|
14
|
+
self.corpus_df = pd.read_parquet(
|
|
15
|
+
os.path.join(project_dir, "data", "corpus.parquet"), engine="pyarrow"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
@result_to_dataframe(["retrieved_contents", "retrieved_ids", "retrieve_scores"])
|
|
19
|
+
def pure(self, previous_result: pd.DataFrame, *args, **kwargs):
|
|
20
|
+
_, contents, scores, ids = self.cast_to_run(previous_result)
|
|
21
|
+
metadatas = fetch_contents(self.corpus_df, ids, column_name="metadata")
|
|
22
|
+
times = [
|
|
23
|
+
[time["last_modified_datetime"] for time in time_list]
|
|
24
|
+
for time_list in metadatas
|
|
25
|
+
]
|
|
26
|
+
top_k = kwargs.pop("top_k")
|
|
27
|
+
return self._pure(contents, scores, ids, top_k, times)
|
|
28
|
+
|
|
29
|
+
def _pure(
|
|
30
|
+
self,
|
|
31
|
+
contents_list: List[List[str]],
|
|
32
|
+
scores_list: List[List[float]],
|
|
33
|
+
ids_list: List[List[str]],
|
|
34
|
+
top_k: int,
|
|
35
|
+
time_list: List[List[datetime]],
|
|
36
|
+
) -> Tuple[List[List[str]], List[List[str]], List[List[float]]]:
|
|
37
|
+
"""
|
|
38
|
+
Rerank the passages based on merely the datetime of the passage.
|
|
39
|
+
It uses 'last_modified_datetime' key in the corpus metadata,
|
|
40
|
+
so the metadata should be in the format of {'last_modified_datetime': datetime.datetime} at the corpus data file.
|
|
41
|
+
|
|
42
|
+
:param contents_list: The list of lists of contents
|
|
43
|
+
:param scores_list: The list of lists of scores from the initial ranking
|
|
44
|
+
:param ids_list: The list of lists of ids
|
|
45
|
+
:param top_k: The number of passages to be retrieved after reranking
|
|
46
|
+
:param time_list: The metadata list of lists of datetime.datetime
|
|
47
|
+
It automatically extracts the 'last_modified_datetime' key from the metadata in the corpus data.
|
|
48
|
+
:return: The reranked contents, ids, and scores
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def sort_row(contents, scores, ids, time, top_k):
|
|
52
|
+
combined = list(zip(contents, scores, ids, time))
|
|
53
|
+
combined.sort(key=lambda x: x[3], reverse=True)
|
|
54
|
+
sorted_contents, sorted_scores, sorted_ids, _ = zip(*combined)
|
|
55
|
+
return (
|
|
56
|
+
list(sorted_contents)[:top_k],
|
|
57
|
+
list(sorted_scores)[:top_k],
|
|
58
|
+
list(sorted_ids)[:top_k],
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
reranked_contents, reranked_scores, reranked_ids = zip(
|
|
62
|
+
*map(
|
|
63
|
+
sort_row,
|
|
64
|
+
contents_list,
|
|
65
|
+
scores_list,
|
|
66
|
+
ids_list,
|
|
67
|
+
time_list,
|
|
68
|
+
[top_k] * len(contents_list),
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
return list(reranked_contents), list(reranked_ids), list(reranked_scores)
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import List, Tuple
|
|
3
|
+
|
|
4
|
+
import pandas as pd
|
|
5
|
+
|
|
6
|
+
from autorag.nodes.passagereranker.base import BasePassageReranker
|
|
7
|
+
from autorag.utils import result_to_dataframe
|
|
8
|
+
from autorag.utils.util import select_top_k, sort_by_scores, empty_cuda_cache
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger("AutoRAG")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Upr(BasePassageReranker):
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
project_dir: str,
|
|
17
|
+
use_bf16: bool = False,
|
|
18
|
+
prefix_prompt: str = "Passage: ",
|
|
19
|
+
suffix_prompt: str = "Please write a question based on this passage.",
|
|
20
|
+
*args,
|
|
21
|
+
**kwargs,
|
|
22
|
+
):
|
|
23
|
+
"""
|
|
24
|
+
Initialize the UPR reranker node.
|
|
25
|
+
|
|
26
|
+
:param project_dir: The project directory
|
|
27
|
+
:param use_bf16: Whether to use bfloat16 for the model. Default is False.
|
|
28
|
+
:param prefix_prompt: The prefix prompt for the language model that generates question for reranking.
|
|
29
|
+
Default is "Passage: ".
|
|
30
|
+
The prefix prompt serves as the initial context or instruction for the language model.
|
|
31
|
+
It sets the stage for what is expected in the output
|
|
32
|
+
:param suffix_prompt: The suffix prompt for the language model that generates question for reranking.
|
|
33
|
+
Default is "Please write a question based on this passage.".
|
|
34
|
+
The suffix prompt provides a cue or a closing instruction to the language model,
|
|
35
|
+
signaling how to conclude the generated text or what format to follow at the end.
|
|
36
|
+
:param kwargs: Extra arguments
|
|
37
|
+
"""
|
|
38
|
+
super().__init__(project_dir, *args, **kwargs)
|
|
39
|
+
|
|
40
|
+
self.scorer = UPRScorer(
|
|
41
|
+
suffix_prompt=suffix_prompt, prefix_prompt=prefix_prompt, use_bf16=use_bf16
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def __del__(self):
|
|
45
|
+
del self.scorer
|
|
46
|
+
super().__del__()
|
|
47
|
+
|
|
48
|
+
@result_to_dataframe(["retrieved_contents", "retrieved_ids", "retrieve_scores"])
|
|
49
|
+
def pure(self, previous_result: pd.DataFrame, *args, **kwargs):
|
|
50
|
+
queries, contents, _, ids = self.cast_to_run(previous_result)
|
|
51
|
+
top_k = kwargs.pop("top_k")
|
|
52
|
+
return self._pure(queries, contents, ids, top_k)
|
|
53
|
+
|
|
54
|
+
def _pure(
|
|
55
|
+
self,
|
|
56
|
+
queries: List[str],
|
|
57
|
+
contents_list: List[List[str]],
|
|
58
|
+
ids_list: List[List[str]],
|
|
59
|
+
top_k: int,
|
|
60
|
+
) -> Tuple[List[List[str]], List[List[str]], List[List[float]]]:
|
|
61
|
+
"""
|
|
62
|
+
Rerank a list of contents based on their relevance to a query using UPR.
|
|
63
|
+
UPR is a reranker based on UPR (https://github.com/DevSinghSachan/unsupervised-passage-reranking).
|
|
64
|
+
The language model will make a question based on the passage and rerank the passages by the likelihood of the question.
|
|
65
|
+
The default model is t5-large.
|
|
66
|
+
|
|
67
|
+
:param queries: The list of queries to use for reranking
|
|
68
|
+
:param contents_list: The list of lists of contents to rerank
|
|
69
|
+
:param ids_list: The list of lists of ids retrieved from the initial ranking
|
|
70
|
+
:param top_k: The number of passages to be retrieved
|
|
71
|
+
|
|
72
|
+
:return: tuple of lists containing the reranked contents, ids, and scores
|
|
73
|
+
"""
|
|
74
|
+
df = pd.DataFrame(
|
|
75
|
+
{
|
|
76
|
+
"query": queries,
|
|
77
|
+
"contents": contents_list,
|
|
78
|
+
"ids": ids_list,
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
df["scores"] = df.apply(
|
|
83
|
+
lambda row: self.scorer.compute(
|
|
84
|
+
query=row["query"], contents=row["contents"]
|
|
85
|
+
),
|
|
86
|
+
axis=1,
|
|
87
|
+
)
|
|
88
|
+
df[["contents", "ids", "scores"]] = df.apply(
|
|
89
|
+
lambda x: sort_by_scores(x, reverse=False), axis=1, result_type="expand"
|
|
90
|
+
)
|
|
91
|
+
results = select_top_k(df, ["contents", "ids", "scores"], top_k)
|
|
92
|
+
return (
|
|
93
|
+
results["contents"].tolist(),
|
|
94
|
+
results["ids"].tolist(),
|
|
95
|
+
results["scores"].tolist(),
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class UPRScorer:
|
|
100
|
+
def __init__(self, suffix_prompt: str, prefix_prompt: str, use_bf16: bool = False):
|
|
101
|
+
try:
|
|
102
|
+
import torch
|
|
103
|
+
from transformers import T5Tokenizer, T5ForConditionalGeneration
|
|
104
|
+
except ImportError:
|
|
105
|
+
raise ImportError(
|
|
106
|
+
"torch is not installed. Please install torch to use UPRReranker."
|
|
107
|
+
)
|
|
108
|
+
model_name = "t5-large"
|
|
109
|
+
self.device = "cuda" if torch.cuda.is_available() else "cpu"
|
|
110
|
+
self.tokenizer = T5Tokenizer.from_pretrained(model_name)
|
|
111
|
+
self.model = T5ForConditionalGeneration.from_pretrained(
|
|
112
|
+
model_name, torch_dtype=torch.bfloat16 if use_bf16 else torch.float32
|
|
113
|
+
).to(self.device)
|
|
114
|
+
self.suffix_prompt = suffix_prompt
|
|
115
|
+
self.prefix_prompt = prefix_prompt
|
|
116
|
+
|
|
117
|
+
def compute(self, query: str, contents: List[str]) -> List[float]:
|
|
118
|
+
try:
|
|
119
|
+
import torch
|
|
120
|
+
except ImportError:
|
|
121
|
+
raise ImportError(
|
|
122
|
+
"torch is not installed. Please install torch to use UPRReranker."
|
|
123
|
+
)
|
|
124
|
+
query_token = self.tokenizer(
|
|
125
|
+
query, max_length=128, truncation=True, return_tensors="pt"
|
|
126
|
+
)
|
|
127
|
+
prompts = list(
|
|
128
|
+
map(
|
|
129
|
+
lambda content: f"{self.prefix_prompt} {content} {self.suffix_prompt}",
|
|
130
|
+
contents,
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
prompt_token_outputs = self.tokenizer(
|
|
134
|
+
prompts,
|
|
135
|
+
padding="longest",
|
|
136
|
+
max_length=512,
|
|
137
|
+
pad_to_multiple_of=8,
|
|
138
|
+
truncation=True,
|
|
139
|
+
return_tensors="pt",
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
query_input_ids = torch.repeat_interleave(
|
|
143
|
+
query_token["input_ids"], len(contents), dim=0
|
|
144
|
+
).to(self.device)
|
|
145
|
+
|
|
146
|
+
with torch.no_grad():
|
|
147
|
+
logits = self.model(
|
|
148
|
+
input_ids=prompt_token_outputs["input_ids"].to(self.device),
|
|
149
|
+
attention_mask=prompt_token_outputs["attention_mask"].to(self.device),
|
|
150
|
+
labels=query_input_ids,
|
|
151
|
+
).logits
|
|
152
|
+
log_softmax = torch.nn.functional.log_softmax(logits, dim=-1)
|
|
153
|
+
nll = -log_softmax.gather(2, query_input_ids.unsqueeze(2)).squeeze(2)
|
|
154
|
+
avg_nll = torch.sum(nll, dim=1)
|
|
155
|
+
return avg_nll.tolist()
|
|
156
|
+
|
|
157
|
+
def __del__(self):
|
|
158
|
+
del self.model
|
|
159
|
+
del self.tokenizer
|
|
160
|
+
empty_cuda_cache()
|