flashrag-dev 0.1.3.dev20241117__tar.gz → 0.1.3.dev20241120__tar.gz

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.
Files changed (59) hide show
  1. {flashrag-dev-0.1.3.dev20241117/flashrag_dev.egg-info → flashrag-dev-0.1.3.dev20241120}/PKG-INFO +1 -1
  2. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/dataset/dataset.py +5 -2
  3. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/dataset/utils.py +2 -0
  4. flashrag-dev-0.1.3.dev20241120/flashrag/generator/fid.py +206 -0
  5. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/generator/generator.py +17 -10
  6. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/pipeline/active_pipeline.py +85 -48
  7. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/pipeline/pipeline.py +8 -6
  8. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/retriever/retriever.py +8 -3
  9. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/retriever/utils.py +17 -0
  10. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/utils/utils.py +1 -1
  11. flashrag-dev-0.1.3.dev20241120/flashrag/version.py +1 -0
  12. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120/flashrag_dev.egg-info}/PKG-INFO +1 -1
  13. flashrag-dev-0.1.3.dev20241117/flashrag/generator/fid.py +0 -247
  14. flashrag-dev-0.1.3.dev20241117/flashrag/version.py +0 -1
  15. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/LICENSE +0 -0
  16. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/README.md +0 -0
  17. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/__init__.py +0 -0
  18. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/config/__init__.py +0 -0
  19. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/config/basic_config.yaml +0 -0
  20. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/config/config.py +0 -0
  21. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/dataset/__init__.py +0 -0
  22. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/evaluator/__init__.py +0 -0
  23. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/evaluator/_bleu.py +0 -0
  24. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/evaluator/evaluator.py +0 -0
  25. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/evaluator/metrics.py +0 -0
  26. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/evaluator/utils.py +0 -0
  27. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/generator/__init__.py +0 -0
  28. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/generator/openai_generator.py +0 -0
  29. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/generator/stop_word_criteria.py +0 -0
  30. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/generator/utils.py +0 -0
  31. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/judger/__init__.py +0 -0
  32. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/judger/judger.py +0 -0
  33. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/pipeline/__init__.py +0 -0
  34. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/pipeline/branching_pipeline.py +0 -0
  35. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/pipeline/replug_utils.py +0 -0
  36. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/prompt/__init__.py +0 -0
  37. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/prompt/base_prompt.py +0 -0
  38. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/prompt/selfask_examplars.py +0 -0
  39. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/prompt/trace_examplars.py +0 -0
  40. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/refiner/__init__.py +0 -0
  41. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/refiner/kg_refiner.py +0 -0
  42. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/refiner/llmlingua_compressor.py +0 -0
  43. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/refiner/refiner.py +0 -0
  44. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/refiner/selective_context_compressor.py +0 -0
  45. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/retriever/__init__.py +0 -0
  46. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/retriever/__main__.py +0 -0
  47. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/retriever/encoder.py +0 -0
  48. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/retriever/index_builder.py +0 -0
  49. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/retriever/reranker.py +0 -0
  50. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/utils/__init__.py +0 -0
  51. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/utils/constants.py +0 -0
  52. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag/utils/pred_parse.py +0 -0
  53. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag_dev.egg-info/SOURCES.txt +0 -0
  54. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag_dev.egg-info/dependency_links.txt +0 -0
  55. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag_dev.egg-info/requires.txt +0 -0
  56. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/flashrag_dev.egg-info/top_level.txt +0 -0
  57. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/pyproject.toml +0 -0
  58. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/setup.cfg +0 -0
  59. {flashrag-dev-0.1.3.dev20241117 → flashrag-dev-0.1.3.dev20241120}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flashrag-dev
3
- Version: 0.1.3.dev20241117
3
+ Version: 0.1.3.dev20241120
4
4
  Summary: A library for efficient Retrieval-Augmented Generation research
5
5
  Home-page: https://github.com/RUC-NLPIR/FlashRAG
6
6
  Author: Jiajie Jin, Yutao Zhu, Chenghao Zhang, Xinyu Yang, Zhicheng Dou
@@ -174,9 +174,12 @@ class Dataset:
174
174
  """Save the dataset into the original format."""
175
175
 
176
176
  save_data = [item.to_dict() for item in self.data]
177
-
177
+ def custom_serializer(obj):
178
+ if isinstance(obj, np.float32):
179
+ return float(obj)
180
+ raise TypeError(f"Type {type(obj)} not serializable")
178
181
  with open(save_path, "w", encoding="utf-8") as f:
179
- json.dump(save_data, f, indent=4)
182
+ json.dump(save_data, f, indent=4, default=custom_serializer)
180
183
 
181
184
  def __str__(self) -> str:
182
185
  """Return a string representation of the dataset with a summary of items."""
@@ -13,6 +13,8 @@ def convert_numpy(obj: Union[Dict, list, np.ndarray, np.generic]) -> Any:
13
13
  return obj.tolist() # Convert numpy arrays to lists
14
14
  elif isinstance(obj, (np.integer, np.floating)):
15
15
  return obj.item() # Convert numpy scalars to native Python scalars
16
+ elif isinstance(obj, np.float32):
17
+ return float(obj)
16
18
  else:
17
19
  return obj # Return the object as-is if it's neither a dict, list, nor numpy type
18
20
 
@@ -0,0 +1,206 @@
1
+ # Source: FiD official repo: https://github.com/facebookresearch/FiD
2
+ # This software is released under Creative Commons public licenses.
3
+
4
+ import torch
5
+ import torch.nn as nn
6
+ import transformers
7
+ import types
8
+ import torch.nn.functional as F
9
+ from torch.nn import CrossEntropyLoss
10
+ import numpy as np
11
+
12
+ class FiDT5(transformers.T5ForConditionalGeneration):
13
+ def __init__(self, config):
14
+ super().__init__(config)
15
+ self.wrap_encoder()
16
+
17
+ def forward_(self, **kwargs):
18
+ if 'input_ids' in kwargs:
19
+ kwargs['input_ids'] = kwargs['input_ids'].view(kwargs['input_ids'].size(0), -1)
20
+ if 'attention_mask' in kwargs:
21
+ kwargs['attention_mask'] = kwargs['attention_mask'].view(kwargs['attention_mask'].size(0), -1)
22
+
23
+ return super(FiDT5, self).forward(
24
+ **kwargs
25
+ )
26
+
27
+ # input ids : bs, n, seq_len -> bs, n*seq_len
28
+ # We need to resize as B x (N * L) instead of (B * N) x L here
29
+ # because the T5 forward method uses the input tensors to infer
30
+ # dimensions used in the decoder.
31
+ # EncoderWrapper resizes the inputs as (B * N) x L.
32
+ def forward(self, input_ids=None, attention_mask=None, **kwargs):
33
+ if input_ids != None:
34
+ # (bs, n, seq_len) -> (bs, n*seq_len)
35
+ # inputs might have already be resized in the generate method
36
+ if input_ids.dim() == 3:
37
+ self.encoder.n_passages = input_ids.size(1)
38
+ input_ids = input_ids.view(input_ids.size(0), -1)
39
+ if attention_mask != None:
40
+ attention_mask = attention_mask.view(attention_mask.size(0), -1)
41
+ #print(input_ids.shape)
42
+ return super().forward(
43
+ input_ids=input_ids,
44
+ attention_mask=attention_mask,
45
+ **kwargs
46
+ )
47
+
48
+ def generate(self, input_ids, attention_mask, **kwargs):
49
+ # input ids - bs, n, seq_len -> bs, n*seq_len
50
+ self.encoder.n_passages = input_ids.size(1)
51
+ return super().generate(
52
+ input_ids=input_ids.view(input_ids.size(0), -1),
53
+ attention_mask=attention_mask.view(attention_mask.size(0), -1),
54
+ **kwargs,
55
+ )
56
+
57
+ def wrap_encoder(self, use_checkpoint=False):
58
+ """
59
+ Wrap T5 encoder to obtain a Fusion-in-Decoder model.
60
+ """
61
+ self.encoder = EncoderWrapper(self.encoder)
62
+
63
+
64
+ def unwrap_encoder(self):
65
+ """
66
+ Unwrap Fusion-in-Decoder encoder, useful to load T5 weights.
67
+ """
68
+ self.encoder = self.encoder.encoder
69
+ block = []
70
+ for mod in self.encoder.block:
71
+ block.append(mod.module)
72
+ block = nn.ModuleList(block)
73
+ self.encoder.block = block
74
+
75
+ def load_t5(self, state_dict):
76
+ self.unwrap_encoder()
77
+ self.load_state_dict(state_dict)
78
+ self.wrap_encoder()
79
+
80
+ def set_checkpoint(self, use_checkpoint):
81
+ """
82
+ Enable or disable checkpointing in the encoder.
83
+ See https://pytorch.org/docs/stable/checkpoint.html
84
+ """
85
+ for mod in self.encoder.encoder.block:
86
+ mod.use_checkpoint = use_checkpoint
87
+
88
+ def tie_weights(self):
89
+ pass
90
+
91
+ class CheckpointWrapper(torch.nn.Module):
92
+ """
93
+ Wrapper replacing None outputs by empty tensors, which allows the use of
94
+ checkpointing.
95
+ """
96
+ def __init__(self, module, use_checkpoint=False):
97
+ super().__init__()
98
+ self.module = module
99
+ self.use_checkpoint = use_checkpoint
100
+
101
+ def forward(self, hidden_states, attention_mask, position_bias, **kwargs):
102
+ if self.use_checkpoint and self.training:
103
+ kwargs = {k: v for k, v in kwargs.items() if v is not None}
104
+ def custom_forward(*inputs):
105
+ output = self.module(*inputs, **kwargs)
106
+ empty = torch.tensor(
107
+ [],
108
+ dtype=torch.float,
109
+ device=output[0].device,
110
+ requires_grad=True)
111
+ output = tuple(x if x is not None else empty for x in output)
112
+ return output
113
+
114
+ output = torch.utils.checkpoint.checkpoint(
115
+ custom_forward,
116
+ hidden_states,
117
+ attention_mask,
118
+ position_bias
119
+ )
120
+ output = tuple(x if x.size() != 0 else None for x in output)
121
+ else:
122
+ output = self.module(hidden_states, attention_mask, position_bias, **kwargs)
123
+ return output
124
+
125
+ def apply_checkpoint_wrapper(t5stack, use_checkpoint):
126
+ """
127
+ Wrap each block of the encoder to enable checkpointing.
128
+ """
129
+ block = []
130
+ for mod in t5stack.block:
131
+ wrapped_mod = CheckpointWrapper(mod, use_checkpoint)
132
+ block.append(wrapped_mod)
133
+ block = nn.ModuleList(block)
134
+ t5stack.block = block
135
+
136
+
137
+ class FiDBart(transformers.BartForConditionalGeneration):
138
+ def __init__(self, config):
139
+ super().__init__(config)
140
+ self.wrap_encoder()
141
+
142
+ def forward(self, input_ids=None, attention_mask=None, **kwargs):
143
+
144
+ if input_ids != None:
145
+ # (bs, n, seq_len) -> (bs, n*seq_len)
146
+ # inputs might have already be resized in the generate method
147
+ if input_ids.dim() == 3:
148
+ self.model.encoder.n_passages = input_ids.size(1)
149
+ input_ids = input_ids.view(input_ids.size(0), -1)
150
+
151
+ if attention_mask != None:
152
+ attention_mask = attention_mask.view(attention_mask.size(0), -1)
153
+
154
+ return super().forward(input_ids=input_ids, attention_mask=attention_mask, **kwargs)
155
+
156
+ def generate(self, input_ids, attention_mask, **kwargs):
157
+ self.model.encoder.n_passages = input_ids.size(1)
158
+ return super().generate(
159
+ input_ids=input_ids.view(input_ids.size(0), -1),
160
+ attention_mask=attention_mask.view(attention_mask.size(0),-1),
161
+ **kwargs)
162
+
163
+ def wrap_encoder(self):
164
+ """
165
+ Wrap T5 encoder to obtain a Fusion-in-Decoder model.
166
+ """
167
+ self.model.encoder = EncoderWrapper(self.model.encoder)
168
+
169
+ def unwrap_encoder(self):
170
+ """
171
+ Unwrap Fusion-in-Decoder encoder, useful to load bart weights.
172
+ """
173
+ self.model.encoder = self.model.encoder.encoder
174
+ block = []
175
+ for mod in self.model.encoder.layers:
176
+ block.append(mod)
177
+ block = nn.ModuleList(block)
178
+ self.model.encoder.layers = block
179
+
180
+ def load_pretrained_model(self, state_dict):
181
+ self.unwrap_encoder()
182
+ self.load_state_dict(state_dict)
183
+ self.wrap_encoder()
184
+ def tie_weights(self):
185
+ pass
186
+
187
+ class EncoderWrapper(torch.nn.Module):
188
+ def __init__(self, encoder,use_checkpoint=False):
189
+ super().__init__()
190
+ self.encoder = encoder
191
+
192
+ try:
193
+ self.main_input_name = encoder.main_input_name
194
+ except:
195
+ pass
196
+ apply_checkpoint_wrapper(self.encoder, use_checkpoint)
197
+
198
+ def forward(self, input_ids=None, attention_mask=None,**kwargs):
199
+ bsz, total_length = input_ids.shape
200
+ passage_length = total_length // self.n_passages
201
+ # total_input
202
+ input_ids = input_ids.view(bsz*self.n_passages, passage_length)
203
+ attention_mask = attention_mask.view(bsz*self.n_passages, passage_length)
204
+ outputs = self.encoder(input_ids=input_ids, attention_mask=attention_mask, **kwargs)
205
+ outputs.last_hidden_state = outputs.last_hidden_state.view(bsz, self.n_passages*passage_length, -1)
206
+ return outputs
@@ -49,10 +49,9 @@ class EncoderDecoderGenerator(BaseGenerator):
49
49
  self.fid = config["use_fid"]
50
50
  model_config = AutoConfig.from_pretrained(self.model_path)
51
51
  arch = model_config.architectures[0].lower()
52
- if "t5" in arch:
52
+ if "t5" in arch or 'fusionindecoder' in arch:
53
53
  if self.fid:
54
54
  from flashrag.generator.fid import FiDT5
55
-
56
55
  self.model = FiDT5.from_pretrained(self.model_path)
57
56
  else:
58
57
  self.model = T5ForConditionalGeneration.from_pretrained(self.model_path)
@@ -65,17 +64,18 @@ class EncoderDecoderGenerator(BaseGenerator):
65
64
  self.tokenizer = AutoTokenizer.from_pretrained(self.model_path)
66
65
 
67
66
  def encode_passages(self, batch_text_passages: List[List[str]]):
67
+ # need size: [batch_size, passage_num, passage_len]
68
68
  passage_ids, passage_masks = [], []
69
- for k, text_passages in enumerate(batch_text_passages):
70
- p = self.tokenizer.batch_encode_plus(
69
+ for text_passages in batch_text_passages:
70
+ p = self.tokenizer(
71
71
  text_passages,
72
72
  max_length=self.max_input_len,
73
- pad_to_max_length=True,
74
- return_tensors="pt",
73
+ padding='max_length',
75
74
  truncation=True,
75
+ return_tensors='pt'
76
76
  )
77
- passage_ids.append(p["input_ids"][None])
78
- passage_masks.append(p["attention_mask"][None])
77
+ passage_ids.append(p['input_ids'][None])
78
+ passage_masks.append(p['attention_mask'][None])
79
79
 
80
80
  passage_ids = torch.cat(passage_ids, dim=0)
81
81
  passage_masks = torch.cat(passage_masks, dim=0)
@@ -128,8 +128,15 @@ class EncoderDecoderGenerator(BaseGenerator):
128
128
  ).to(self.device)
129
129
 
130
130
  # TODO: multi-gpu inference
131
- outputs = self.model.generate(**inputs, **generation_params)
132
-
131
+ if self.fid:
132
+ if 'max_new_tokens' in generation_params:
133
+ max_new_tokens = generation_params.pop('max_new_tokens')
134
+ else:
135
+ max_new_tokens = 32
136
+
137
+ outputs = self.model.generate(**inputs, max_new_tokens=max_new_tokens, pad_token_id=self.tokenizer.pad_token_id, decoder_start_token_id=self.tokenizer.pad_token_id)
138
+ else:
139
+ outputs = self.model.generate(**inputs, **generation_params)
133
140
  outputs = self.tokenizer.batch_decode(
134
141
  outputs,
135
142
  skip_special_tokens=True,
@@ -898,12 +898,12 @@ class SelfAskPipeline(BasicPipeline):
898
898
  dataset = self.evaluate(dataset, do_eval=do_eval, pred_process_fun=pred_process_fun)
899
899
  return dataset
900
900
 
901
-
901
+
902
902
  class IRCOTPipeline(BasicPipeline):
903
903
  IRCOT_INSTRUCTION = 'You serve as an intelligent assistant, adept at facilitating users through complex, multi-hop reasoning across multiple documents. This task is illustrated through demonstrations, each consisting of a document set paired with a relevant question and its multi-hop reasoning thoughts. Your task is to generate one thought for current step, DON\'T generate the whole thoughts at once! If you reach what you believe to be the final step, start with "So the answer is:".'
904
904
  IRCOT_EXAMPLE = "Wikipedia Title: Kurram Garhi\nKurram Garhi is a small village located near the city of Bannu, which is the part of Khyber Pakhtunkhwa province of Pakistan. Its population is approximately 35000. Barren hills are near this village. This village is on the border of Kurram Agency. Other nearby villages are Peppal, Surwangi and Amandi Kala.\n\nWikipedia Title: 2001–02 UEFA Champions League second group stage\nEight winners and eight runners- up from the first group stage were drawn into four groups of four teams, each containing two group winners and two runners- up. Teams from the same country or from the same first round group could not be drawn together. The top two teams in each group advanced to the quarter- finals.\n\nWikipedia Title: Satellite tournament\nA satellite tournament is either a minor tournament or event on a competitive sporting tour or one of a group of such tournaments that form a series played in the same country or region.\n\nWikipedia Title: Trojkrsti\nTrojkrsti is a village in Municipality of Prilep, Republic of Macedonia.\n\nWikipedia Title: Telephone numbers in Ascension Island\nCountry Code:+ 247< br> International Call Prefix: 00 Ascension Island does not share the same country code( +290) with the rest of St Helena.\n\nQuestion: Are both Kurram Garhi and Trojkrsti located in the same country?\nThought: Kurram Garhi is located in the country of Pakistan. Trojkrsti is located in the country of Republic of Macedonia. Thus, they are not in the same country. So the answer is: no.\n\n"
905
905
 
906
- def __init__(self, config, prompt_template=None, retriever=None, generator=None, max_iter=2):
906
+ def __init__(self, config, prompt_template=None, retriever=None, generator=None, max_iter=5):
907
907
  # if not provide prompt template, use default template provided by IRCOT
908
908
  if prompt_template is None:
909
909
  prompt_template = PromptTemplate(
@@ -920,60 +920,97 @@ class IRCOTPipeline(BasicPipeline):
920
920
 
921
921
  self.max_iter = max_iter
922
922
 
923
- def run_item(self, item):
924
- question = item.question
925
- retrieval_result, scores = self.retriever.search(question, return_score=True)
926
- doc2score = {doc_item["id"]: score for doc_item, score in zip(retrieval_result, scores)}
927
- id2doc = {doc_item["id"]: doc_item for doc_item in retrieval_result}
928
-
929
- thoughts = []
923
+ def run_batch(self, items):
924
+ # Initialize the necessary data structures
925
+ batch_thoughts = {item_id: [] for item_id in range(len(items))}
930
926
  iter_num = 0
927
+ batch_retrieval_results = []
928
+ doc2score_batch = []
929
+ id2doc_batch = []
930
+
931
+ # Initial retrieval for all items in the batch
932
+ questions = [item.question for item in items]
933
+ retrieval_results, scoress = self.retriever.batch_search(questions, return_score=True)
934
+ for retrieval_result, scores in zip(retrieval_results,scoress):
935
+
936
+ doc2score = {doc_item['id']: score for doc_item, score in zip(retrieval_result, scores)}
937
+ id2doc = {doc_item['id']: doc_item for doc_item in retrieval_result}
938
+ batch_retrieval_results.append(retrieval_result)
939
+ doc2score_batch.append(doc2score)
940
+ id2doc_batch.append(id2doc)
941
+
942
+ # Start the iterative process
943
+ active_item_ids = list(range(len(items))) # Track items that need more iterations
931
944
  while iter_num < self.max_iter:
932
- input_prompt = self.prompt_template.get_string(
933
- question=question, retrieval_result=retrieval_result, previous_gen=" ".join(thoughts)
934
- )
935
- new_thought = self.generator.generate(input_prompt,stop=['.', '\n'])[0]
936
- thoughts.append(new_thought)
937
- iter_num += 1
938
- if "So the answer is:" in new_thought:
939
- item.update_output(
940
- f"intermediate_output_iter{iter_num}",
941
- {
942
- "input_prompt": input_prompt,
943
- "new_thought": new_thought,
944
- },
945
+ # Generate prompts and new thoughts for the active items
946
+ input_prompts = [
947
+ self.prompt_template.get_string(
948
+ question=items[item_id].question,
949
+ retrieval_result=batch_retrieval_results[item_id],
950
+ previous_gen=' '.join(batch_thoughts[item_id])
945
951
  )
946
- break
952
+ for item_id in active_item_ids
953
+ ]
947
954
 
948
- # retrieve new docs and merge
949
- new_retrieval_result, new_scores = self.retriever.search(new_thought, return_score=True)
950
- for doc_item, score in zip(new_retrieval_result, new_scores):
951
- id2doc[doc_item["id"]] = doc_item
952
- doc_id = doc_item["id"]
953
- if doc_id in doc2score:
954
- doc2score[doc_id] = max(doc2score[doc_id], score)
955
+ # Batch generation for active items
956
+ new_thoughts_batch = self.generator.generate(input_prompts, stop=['.', '\n'])
957
+
958
+ # Update thoughts and determine next active items
959
+ new_active_item_ids = []
960
+ for idx, item_id in enumerate(active_item_ids):
961
+ new_thought = new_thoughts_batch[idx]
962
+ batch_thoughts[item_id].append(new_thought)
963
+
964
+ # Check for termination condition
965
+ # Store intermediate outputs
966
+ if "So the answer is:" in new_thought:
967
+ items[item_id].update_output(
968
+ f'intermediate_output_iter{iter_num}',
969
+ {
970
+ 'input_prompt': input_prompts[idx],
971
+ 'new_thought': new_thought,
972
+ },
973
+ )
955
974
  else:
956
- doc2score[doc_id] = score
957
- sorted_doc_score = sorted(doc2score.items(), key=lambda x: x[1], reverse=False)
958
- sorted_doc_id = [t[0] for t in sorted_doc_score]
959
- retrieval_result = [id2doc[id] for id in sorted_doc_id]
960
-
961
- item.update_output(
962
- f"intermediate_output_iter{iter_num}",
963
- {
964
- "input_prompt": input_prompt,
965
- "new_thought": new_thought,
966
- "new_retreival_result": new_retrieval_result,
967
- },
968
- )
975
+ new_active_item_ids.append(item_id)
976
+
977
+ # Update active item IDs for the next iteration
978
+ active_item_ids = new_active_item_ids
979
+
980
+ # Perform batch retrieval for new thoughts of active items
981
+ if active_item_ids:
982
+ print(len(active_item_ids))
983
+ print(active_item_ids)
984
+ new_thoughts_for_retrieval = [batch_thoughts[item_id][-1] for item_id in active_item_ids]
985
+ new_retrieval_results, new_scoress = self.retriever.batch_search(new_thoughts_for_retrieval, return_score=True)
986
+
987
+ for i, item_id in enumerate(active_item_ids):
988
+ new_retrieval_result, new_scores = new_retrieval_results[i],new_scoress[i]
989
+
990
+ # Update doc2score and id2doc for the current item
991
+ for doc_item, score in zip(new_retrieval_result, new_scores):
992
+ doc_id = doc_item['id']
993
+ id2doc_batch[item_id][doc_id] = doc_item
994
+ if doc_id in doc2score_batch[item_id]:
995
+ doc2score_batch[item_id][doc_id] = max(doc2score_batch[item_id][doc_id], score)
996
+ else:
997
+ doc2score_batch[item_id][doc_id] = score
969
998
 
970
- item.update_output("retrieval_result", retrieval_result)
971
- item.update_output("pred", " ".join(thoughts))
972
- return item
999
+ # Sort and update retrieval results
1000
+ sorted_doc_score = sorted(doc2score_batch[item_id].items(), key=lambda x: x[1], reverse=False)
1001
+ sorted_doc_id = [t[0] for t in sorted_doc_score]
1002
+ batch_retrieval_results[item_id] = [id2doc_batch[item_id][id] for id in sorted_doc_id]
1003
+
1004
+ iter_num += 1
1005
+
1006
+ # Final update for each item in the batch
1007
+ for item_id, item in enumerate(items):
1008
+ item.update_output('retrieval_result', batch_retrieval_results[item_id])
1009
+ item.update_output('pred', ' '.join(batch_thoughts[item_id]))
973
1010
 
974
1011
  def run(self, dataset, do_eval=True, pred_process_fun=ircot_pred_parse):
975
- for item in tqdm(dataset, desc="Inference: "):
976
- self.run_item(item)
1012
+
1013
+ self.run_batch(dataset)
977
1014
 
978
1015
  dataset = self.evaluate(dataset, do_eval=do_eval, pred_process_fun=pred_process_fun)
979
1016
  return dataset
@@ -107,11 +107,11 @@ class SequentialPipeline(BasicPipeline):
107
107
  ]
108
108
 
109
109
  else:
110
- input_prompts = [
111
- self.prompt_template.get_string(question=q, retrieval_result=r)
112
- for q, r in zip(dataset.question, dataset.retrieval_result)
113
- ]
114
- dataset.update_output("prompt", input_prompts)
110
+ if not self.use_fid:
111
+ input_prompts = [
112
+ self.prompt_template.get_string(question=q, retrieval_result=r)
113
+ for q, r in zip(dataset.question, dataset.retrieval_result)
114
+ ]
115
115
 
116
116
  if self.use_fid:
117
117
  print("Use FiD generation")
@@ -119,7 +119,9 @@ class SequentialPipeline(BasicPipeline):
119
119
  for item in dataset:
120
120
  q = item.question
121
121
  docs = item.retrieval_result
122
- input_prompts.append([q + " " + doc for doc in docs])
122
+ input_prompts.append([q + " " + doc['contents'] for doc in docs])
123
+ dataset.update_output("prompt", input_prompts)
124
+
123
125
  # delete used refiner to release memory
124
126
  if self.refiner:
125
127
  del self.refiner
@@ -7,9 +7,9 @@ from typing import List, Dict
7
7
  import functools
8
8
  from tqdm import tqdm
9
9
  import faiss
10
-
10
+ import numpy as np
11
11
  from flashrag.utils import get_reranker
12
- from flashrag.retriever.utils import load_corpus, load_docs
12
+ from flashrag.retriever.utils import load_corpus, load_docs, convert_numpy
13
13
  from flashrag.retriever.encoder import Encoder, STEncoder
14
14
 
15
15
 
@@ -136,8 +136,13 @@ class BaseRetriever:
136
136
  self.cache = json.load(f)
137
137
 
138
138
  def _save_cache(self):
139
+ self.cache = convert_numpy(self.cache)
140
+ def custom_serializer(obj):
141
+ if isinstance(obj, np.float32):
142
+ return float(obj)
143
+ raise TypeError(f"Type {type(obj)} not serializable")
139
144
  with open(self.cache_save_path, "w") as f:
140
- json.dump(self.cache, f, indent=4)
145
+ json.dump(self.cache, f, indent=4, default=custom_serializer)
141
146
 
142
147
  def _search(self, query: str, num: int, return_score: bool) -> List[Dict[str, str]]:
143
148
  r"""Retrieve topk relevant documents in corpus.
@@ -1,8 +1,25 @@
1
1
  import json
2
2
  import warnings
3
+ from typing import Dict, Any, Union, List, Dict
4
+ import numpy as np
3
5
  import datasets
4
6
  from transformers import AutoTokenizer, AutoModel, AutoConfig
5
7
 
8
+ def convert_numpy(obj: Union[Dict, list, np.ndarray, np.generic]) -> Any:
9
+ """Recursively convert numpy objects in nested dictionaries or lists to native Python types."""
10
+ if isinstance(obj, dict):
11
+ return {k: convert_numpy(v) for k, v in obj.items()}
12
+ elif isinstance(obj, list):
13
+ return [convert_numpy(i) for i in obj]
14
+ elif isinstance(obj, np.ndarray):
15
+ return obj.tolist() # Convert numpy arrays to lists
16
+ elif isinstance(obj, (np.integer, np.floating)):
17
+ return obj.item() # Convert numpy scalars to native Python scalars
18
+ elif isinstance(obj, np.float32):
19
+ return float(obj)
20
+ else:
21
+ return obj # Return the object as-is if it's neither a dict, list, nor numpy type
22
+
6
23
 
7
24
  def load_model(model_path: str, use_fp16: bool = False):
8
25
  model_config = AutoConfig.from_pretrained(model_path, trust_remote_code=True)
@@ -36,7 +36,7 @@ def get_generator(config, **params):
36
36
  elif config["framework"] == "hf":
37
37
  model_config = AutoConfig.from_pretrained(config["generator_model_path"])
38
38
  arch = model_config.architectures[0]
39
- if "t5" in arch.lower() or "bart" in arch.lower():
39
+ if "t5" in arch.lower() or "bart" in arch.lower() or 'fusionindecoder' in arch.lower():
40
40
  return getattr(importlib.import_module("flashrag.generator"), "EncoderDecoderGenerator")(config, **params)
41
41
  else:
42
42
  return getattr(importlib.import_module("flashrag.generator"), "HFCausalLMGenerator")(config, **params)
@@ -0,0 +1 @@
1
+ __version__ = "0.1.3dev20241120"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flashrag-dev
3
- Version: 0.1.3.dev20241117
3
+ Version: 0.1.3.dev20241120
4
4
  Summary: A library for efficient Retrieval-Augmented Generation research
5
5
  Home-page: https://github.com/RUC-NLPIR/FlashRAG
6
6
  Author: Jiajie Jin, Yutao Zhu, Chenghao Zhang, Xinyu Yang, Zhicheng Dou
@@ -1,247 +0,0 @@
1
- # Source: FiD official repo: https://github.com/facebookresearch/FiD
2
- # This software is released under Creative Commons public licenses.
3
-
4
- import types
5
- import torch
6
- import transformers
7
- import torch.nn.functional as F
8
- from torch import nn
9
-
10
-
11
- class FiDT5(transformers.T5ForConditionalGeneration):
12
- def __init__(self, config):
13
- super().__init__(config)
14
- self.wrap_encoder()
15
-
16
- def forward_(self, **kwargs):
17
- if "input_ids" in kwargs:
18
- kwargs["input_ids"] = kwargs["input_ids"].view(kwargs["input_ids"].size(0), -1)
19
- if "attention_mask" in kwargs:
20
- kwargs["attention_mask"] = kwargs["attention_mask"].view(kwargs["attention_mask"].size(0), -1)
21
-
22
- return super(FiDT5, self).forward(**kwargs)
23
-
24
- # We need to resize as B x (N * L) instead of (B * N) x L here
25
- # because the T5 forward method uses the input tensors to infer
26
- # dimensions used in the decoder.
27
- # EncoderWrapper resizes the inputs as (B * N) x L.
28
- def forward(self, input_ids=None, attention_mask=None, **kwargs):
29
- if input_ids != None:
30
- # inputs might have already be resized in the generate method
31
- if input_ids.dim() == 3:
32
- self.encoder.n_passages = input_ids.size(1)
33
- input_ids = input_ids.view(input_ids.size(0), -1)
34
- if attention_mask != None:
35
- attention_mask = attention_mask.view(attention_mask.size(0), -1)
36
- return super().forward(input_ids=input_ids, attention_mask=attention_mask, **kwargs)
37
-
38
- # We need to resize the inputs here, as the generate method expect 2D tensors
39
- def generate(self, input_ids, attention_mask, max_length):
40
- self.encoder.n_passages = input_ids.size(1)
41
- return super().generate(
42
- input_ids=input_ids.view(input_ids.size(0), -1),
43
- attention_mask=attention_mask.view(attention_mask.size(0), -1),
44
- max_length=max_length,
45
- )
46
-
47
- def wrap_encoder(self, use_checkpoint=False):
48
- """
49
- Wrap T5 encoder to obtain a Fusion-in-Decoder model.
50
- """
51
- self.encoder = EncoderWrapper(self.encoder, use_checkpoint=use_checkpoint)
52
-
53
- def unwrap_encoder(self):
54
- """
55
- Unwrap Fusion-in-Decoder encoder, useful to load T5 weights.
56
- """
57
- self.encoder = self.encoder.encoder
58
- block = []
59
- for mod in self.encoder.block:
60
- block.append(mod.module)
61
- block = nn.ModuleList(block)
62
- self.encoder.block = block
63
-
64
- def load_t5(self, state_dict):
65
- self.unwrap_encoder()
66
- self.load_state_dict(state_dict)
67
- self.wrap_encoder()
68
-
69
- def set_checkpoint(self, use_checkpoint):
70
- """
71
- Enable or disable checkpointing in the encoder.
72
- See https://pytorch.org/docs/stable/checkpoint.html
73
- """
74
- for mod in self.encoder.encoder.block:
75
- mod.use_checkpoint = use_checkpoint
76
-
77
- def reset_score_storage(self):
78
- """
79
- Reset score storage, only used when cross-attention scores are saved
80
- to train a retriever.
81
- """
82
- for mod in self.decoder.block:
83
- mod.layer[1].EncDecAttention.score_storage = None
84
-
85
- def get_crossattention_scores(self, context_mask):
86
- """
87
- Cross-attention scores are aggregated to obtain a single scalar per
88
- passage. This scalar can be seen as a similarity score between the
89
- question and the input passage. It is obtained by averaging the
90
- cross-attention scores obtained on the first decoded token over heads,
91
- layers, and tokens of the input passage.
92
-
93
- More details in Distilling Knowledge from Reader to Retriever:
94
- https://arxiv.org/abs/2012.04584.
95
- """
96
- scores = []
97
- n_passages = context_mask.size(1)
98
- for mod in self.decoder.block:
99
- scores.append(mod.layer[1].EncDecAttention.score_storage)
100
- scores = torch.cat(scores, dim=2)
101
- bsz, n_heads, n_layers, _ = scores.size()
102
- # batch_size, n_head, n_layers, n_passages, text_maxlength
103
- scores = scores.view(bsz, n_heads, n_layers, n_passages, -1)
104
- scores = scores.masked_fill(~context_mask[:, None, None], 0.0)
105
- scores = scores.sum(dim=[1, 2, 4])
106
- ntokens = context_mask.sum(dim=[2]) * n_layers * n_heads
107
- scores = scores / ntokens
108
- return scores
109
-
110
- def overwrite_forward_crossattention(self):
111
- """
112
- Replace cross-attention forward function, only used to save
113
- cross-attention scores.
114
- """
115
- for mod in self.decoder.block:
116
- attn = mod.layer[1].EncDecAttention
117
- attn.forward = types.MethodType(cross_attention_forward, attn)
118
-
119
-
120
- class EncoderWrapper(torch.nn.Module):
121
- """
122
- Encoder Wrapper for T5 Wrapper to obtain a Fusion-in-Decoder model.
123
- """
124
-
125
- def __init__(self, encoder, use_checkpoint=False):
126
- super().__init__()
127
-
128
- self.encoder = encoder
129
- apply_checkpoint_wrapper(self.encoder, use_checkpoint)
130
-
131
- def forward(
132
- self,
133
- input_ids=None,
134
- attention_mask=None,
135
- **kwargs,
136
- ):
137
- # total_length = n_passages * passage_length
138
- bsz, total_length = input_ids.shape
139
- passage_length = total_length // self.n_passages
140
- input_ids = input_ids.view(bsz * self.n_passages, passage_length)
141
- attention_mask = attention_mask.view(bsz * self.n_passages, passage_length)
142
- outputs = self.encoder(input_ids, attention_mask, **kwargs)
143
- outputs = (outputs[0].view(bsz, self.n_passages * passage_length, -1),) + outputs[1:]
144
- return outputs
145
-
146
-
147
- class CheckpointWrapper(torch.nn.Module):
148
- """
149
- Wrapper replacing None outputs by empty tensors, which allows the use of
150
- checkpointing.
151
- """
152
-
153
- def __init__(self, module, use_checkpoint=False):
154
- super().__init__()
155
- self.module = module
156
- self.use_checkpoint = use_checkpoint
157
-
158
- def forward(self, hidden_states, attention_mask, position_bias, **kwargs):
159
- if self.use_checkpoint and self.training:
160
- kwargs = {k: v for k, v in kwargs.items() if v is not None}
161
-
162
- def custom_forward(*inputs):
163
- output = self.module(*inputs, **kwargs)
164
- empty = torch.tensor([], dtype=torch.float, device=output[0].device, requires_grad=True)
165
- output = tuple(x if x is not None else empty for x in output)
166
- return output
167
-
168
- output = torch.utils.checkpoint.checkpoint(custom_forward, hidden_states, attention_mask, position_bias)
169
- output = tuple(x if x.size() != 0 else None for x in output)
170
- else:
171
- output = self.module(hidden_states, attention_mask, position_bias, **kwargs)
172
- return output
173
-
174
-
175
- def apply_checkpoint_wrapper(t5stack, use_checkpoint):
176
- """
177
- Wrap each block of the encoder to enable checkpointing.
178
- """
179
- block = []
180
- for mod in t5stack.block:
181
- wrapped_mod = CheckpointWrapper(mod, use_checkpoint)
182
- block.append(wrapped_mod)
183
- block = nn.ModuleList(block)
184
- t5stack.block = block
185
-
186
-
187
- def cross_attention_forward(
188
- self,
189
- input,
190
- mask=None,
191
- kv=None,
192
- position_bias=None,
193
- past_key_value_state=None,
194
- head_mask=None,
195
- query_length=None,
196
- use_cache=False,
197
- output_attentions=False,
198
- ):
199
- """
200
- This only works for computing cross attention over the input
201
- """
202
- assert kv != None
203
- assert head_mask == None
204
- assert position_bias != None or self.has_relative_attention_bias
205
-
206
- bsz, qlen, dim = input.size()
207
- n_heads, d_heads = self.n_heads, self.d_kv
208
- klen = kv.size(1)
209
-
210
- q = self.q(input).view(bsz, -1, n_heads, d_heads).transpose(1, 2)
211
- if past_key_value_state == None:
212
- k = self.k(kv).view(bsz, -1, n_heads, d_heads).transpose(1, 2)
213
- v = self.v(kv).view(bsz, -1, n_heads, d_heads).transpose(1, 2)
214
- else:
215
- k, v = past_key_value_state
216
-
217
- scores = torch.einsum("bnqd,bnkd->bnqk", q, k)
218
-
219
- if mask is not None:
220
- scores += mask
221
-
222
- if position_bias is None:
223
- position_bias = self.compute_bias(qlen, klen)
224
- scores += position_bias
225
-
226
- if self.score_storage is None:
227
- self.score_storage = scores
228
-
229
- attn = F.softmax(scores.float(), dim=-1).type_as(scores)
230
- attn = F.dropout(attn, p=self.dropout, training=self.training)
231
-
232
- output = torch.matmul(attn, v)
233
- output = output.transpose(1, 2).contiguous().view(bsz, -1, self.inner_dim)
234
- output = self.o(output)
235
-
236
- if use_cache:
237
- output = (output,) + ((k, v),)
238
- else:
239
- output = (output,) + (None,)
240
-
241
- if output_attentions:
242
- output = output + (attn,)
243
-
244
- if self.has_relative_attention_bias:
245
- output = output + (position_bias,)
246
-
247
- return output
@@ -1 +0,0 @@
1
- __version__ = "0.1.3dev20241117"