flashrag-dev 0.1.1__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.
- flashrag/__init__.py +0 -0
- flashrag/config/__init__.py +2 -0
- flashrag/config/config.py +219 -0
- flashrag/dataset/__init__.py +2 -0
- flashrag/dataset/dataset.py +160 -0
- flashrag/dataset/utils.py +60 -0
- flashrag/evaluator/__init__.py +2 -0
- flashrag/evaluator/_bleu.py +209 -0
- flashrag/evaluator/evaluator.py +82 -0
- flashrag/evaluator/metrics.py +508 -0
- flashrag/evaluator/utils.py +19 -0
- flashrag/generator/__init__.py +2 -0
- flashrag/generator/fid.py +247 -0
- flashrag/generator/generator.py +623 -0
- flashrag/generator/openai_generator.py +96 -0
- flashrag/generator/stop_word_criteria.py +105 -0
- flashrag/judger/__init__.py +1 -0
- flashrag/judger/judger.py +184 -0
- flashrag/pipeline/__init__.py +3 -0
- flashrag/pipeline/active_pipeline.py +980 -0
- flashrag/pipeline/branching_pipeline.py +250 -0
- flashrag/pipeline/pipeline.py +248 -0
- flashrag/pipeline/replug_utils.py +249 -0
- flashrag/prompt/__init__.py +1 -0
- flashrag/prompt/base_prompt.py +165 -0
- flashrag/prompt/selfask_examplars.py +108 -0
- flashrag/prompt/trace_examplars.py +4015 -0
- flashrag/refiner/__init__.py +2 -0
- flashrag/refiner/kg_refiner.py +612 -0
- flashrag/refiner/llmlingua_compressor.py +2360 -0
- flashrag/refiner/refiner.py +252 -0
- flashrag/refiner/selective_context_compressor.py +294 -0
- flashrag/retriever/__init__.py +3 -0
- flashrag/retriever/__main__.py +4 -0
- flashrag/retriever/encoder.py +120 -0
- flashrag/retriever/index_builder.py +334 -0
- flashrag/retriever/reranker.py +145 -0
- flashrag/retriever/retriever.py +346 -0
- flashrag/retriever/utils.py +49 -0
- flashrag/utils/__init__.py +2 -0
- flashrag/utils/constants.py +51 -0
- flashrag/utils/pred_parse.py +25 -0
- flashrag/utils/utils.py +135 -0
- flashrag_dev-0.1.1.dist-info/LICENSE +21 -0
- flashrag_dev-0.1.1.dist-info/METADATA +616 -0
- flashrag_dev-0.1.1.dist-info/RECORD +48 -0
- flashrag_dev-0.1.1.dist-info/WHEEL +5 -0
- flashrag_dev-0.1.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
from copy import deepcopy
|
|
3
|
+
from tqdm import tqdm
|
|
4
|
+
from tqdm.auto import trange
|
|
5
|
+
import numpy as np
|
|
6
|
+
import torch
|
|
7
|
+
from transformers import (
|
|
8
|
+
AutoTokenizer,
|
|
9
|
+
AutoModelForCausalLM,
|
|
10
|
+
T5ForConditionalGeneration,
|
|
11
|
+
BartForConditionalGeneration,
|
|
12
|
+
AutoConfig,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BaseGenerator:
|
|
17
|
+
"""`BaseGenerator` is a base object of Generator model."""
|
|
18
|
+
|
|
19
|
+
def __init__(self, config):
|
|
20
|
+
self.model_name = config["generator_model"]
|
|
21
|
+
self.model_path = config["generator_model_path"]
|
|
22
|
+
|
|
23
|
+
self.max_input_len = config["generator_max_input_len"]
|
|
24
|
+
self.batch_size = config["generator_batch_size"]
|
|
25
|
+
self.device = config["device"]
|
|
26
|
+
self.gpu_num = torch.cuda.device_count()
|
|
27
|
+
|
|
28
|
+
self.generation_params = config["generation_params"]
|
|
29
|
+
|
|
30
|
+
def generate(self, input_list: list) -> List[str]:
|
|
31
|
+
"""Get responses from the generater.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
input_list: it contains input texts, each item represents a sample.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
list: contains generator's response of each input sample.
|
|
38
|
+
"""
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class EncoderDecoderGenerator(BaseGenerator):
|
|
43
|
+
"""Class for encoder-decoder model"""
|
|
44
|
+
|
|
45
|
+
def __init__(self, config):
|
|
46
|
+
super().__init__(config)
|
|
47
|
+
self.fid = config["use_fid"]
|
|
48
|
+
model_config = AutoConfig.from_pretrained(self.model_path)
|
|
49
|
+
arch = model_config.architectures[0].lower()
|
|
50
|
+
if "t5" in arch:
|
|
51
|
+
if self.fid:
|
|
52
|
+
from flashrag.generator.fid import FiDT5
|
|
53
|
+
|
|
54
|
+
self.model = FiDT5.from_pretrained(self.model_path)
|
|
55
|
+
else:
|
|
56
|
+
self.model = T5ForConditionalGeneration.from_pretrained(
|
|
57
|
+
self.model_path
|
|
58
|
+
)
|
|
59
|
+
else:
|
|
60
|
+
if self.fid:
|
|
61
|
+
assert False, "FiD only support T5"
|
|
62
|
+
self.model = BartForConditionalGeneration.from_pretrained(
|
|
63
|
+
self.model_path
|
|
64
|
+
)
|
|
65
|
+
self.model.cuda()
|
|
66
|
+
self.model.eval()
|
|
67
|
+
self.tokenizer = AutoTokenizer.from_pretrained(self.model_path)
|
|
68
|
+
|
|
69
|
+
def encode_passages(self, batch_text_passages: List[List[str]]):
|
|
70
|
+
passage_ids, passage_masks = [], []
|
|
71
|
+
for k, text_passages in enumerate(batch_text_passages):
|
|
72
|
+
p = self.tokenizer.batch_encode_plus(
|
|
73
|
+
text_passages,
|
|
74
|
+
max_length=self.max_input_len,
|
|
75
|
+
pad_to_max_length=True,
|
|
76
|
+
return_tensors="pt",
|
|
77
|
+
truncation=True,
|
|
78
|
+
)
|
|
79
|
+
passage_ids.append(p["input_ids"][None])
|
|
80
|
+
passage_masks.append(p["attention_mask"][None])
|
|
81
|
+
|
|
82
|
+
passage_ids = torch.cat(passage_ids, dim=0)
|
|
83
|
+
passage_masks = torch.cat(passage_masks, dim=0)
|
|
84
|
+
return passage_ids, passage_masks.bool()
|
|
85
|
+
|
|
86
|
+
@torch.inference_mode(mode=True)
|
|
87
|
+
def generate(self, input_list: List, batch_size=None, **params):
|
|
88
|
+
if isinstance(input_list, str):
|
|
89
|
+
input_list = [input_list]
|
|
90
|
+
if batch_size is None:
|
|
91
|
+
batch_size = self.batch_size
|
|
92
|
+
|
|
93
|
+
generation_params = deepcopy(self.generation_params)
|
|
94
|
+
generation_params.update(params)
|
|
95
|
+
|
|
96
|
+
# deal stop params
|
|
97
|
+
stop_sym = None
|
|
98
|
+
if "stop" in generation_params:
|
|
99
|
+
from flashrag.generator.stop_word_criteria import StopWordCriteria
|
|
100
|
+
|
|
101
|
+
stop_sym = generation_params.pop("stop")
|
|
102
|
+
stopping_criteria = [
|
|
103
|
+
StopWordCriteria(
|
|
104
|
+
tokenizer=self.tokenizer,
|
|
105
|
+
prompts=input_list,
|
|
106
|
+
stop_words=stop_sym,
|
|
107
|
+
)
|
|
108
|
+
]
|
|
109
|
+
generation_params["stopping_criteria"] = stopping_criteria
|
|
110
|
+
|
|
111
|
+
max_tokens = params.pop("max_tokens", None) or params.pop(
|
|
112
|
+
"max_new_tokens", None
|
|
113
|
+
)
|
|
114
|
+
if max_tokens is not None:
|
|
115
|
+
generation_params["max_new_tokens"] = max_tokens
|
|
116
|
+
else:
|
|
117
|
+
generation_params["max_new_tokens"] = generation_params.get(
|
|
118
|
+
"max_new_tokens", generation_params.pop("max_tokens", None)
|
|
119
|
+
)
|
|
120
|
+
generation_params.pop("max_tokens", None)
|
|
121
|
+
|
|
122
|
+
responses = []
|
|
123
|
+
for idx in trange(
|
|
124
|
+
0, len(input_list), batch_size, desc="Generation process: "
|
|
125
|
+
):
|
|
126
|
+
batched_prompts = input_list[idx : idx + batch_size]
|
|
127
|
+
if self.fid:
|
|
128
|
+
# assume each input in input_list is a list, contains K string
|
|
129
|
+
input_ids, attention_mask = self.encode_passages(
|
|
130
|
+
batched_prompts
|
|
131
|
+
)
|
|
132
|
+
inputs = {
|
|
133
|
+
"input_ids": input_ids.to(self.device),
|
|
134
|
+
"attention_mask": attention_mask.to(self.device),
|
|
135
|
+
}
|
|
136
|
+
else:
|
|
137
|
+
inputs = self.tokenizer(
|
|
138
|
+
batched_prompts,
|
|
139
|
+
return_tensors="pt",
|
|
140
|
+
padding=True,
|
|
141
|
+
truncation=True,
|
|
142
|
+
max_length=self.max_input_len,
|
|
143
|
+
).to(self.device)
|
|
144
|
+
|
|
145
|
+
# TODO: multi-gpu inference
|
|
146
|
+
outputs = self.model.generate(**inputs, **generation_params)
|
|
147
|
+
|
|
148
|
+
outputs = self.tokenizer.batch_decode(
|
|
149
|
+
outputs,
|
|
150
|
+
skip_special_tokens=True,
|
|
151
|
+
clean_up_tokenization_spaces=False,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
responses += outputs
|
|
155
|
+
|
|
156
|
+
return responses
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class VLLMGenerator(BaseGenerator):
|
|
160
|
+
"""Class for decoder-only generator, based on vllm."""
|
|
161
|
+
|
|
162
|
+
def __init__(self, config):
|
|
163
|
+
super().__init__(config)
|
|
164
|
+
|
|
165
|
+
from vllm import LLM
|
|
166
|
+
|
|
167
|
+
if "gpu_memory_utilization" not in config:
|
|
168
|
+
gpu_memory_utilization = 0.85
|
|
169
|
+
else:
|
|
170
|
+
gpu_memory_utilization = config["gpu_memory_utilization"]
|
|
171
|
+
if self.gpu_num != 1 and self.gpu_num % 2 != 0:
|
|
172
|
+
tensor_parallel_size = self.gpu_num - 1
|
|
173
|
+
else:
|
|
174
|
+
tensor_parallel_size = self.gpu_num
|
|
175
|
+
|
|
176
|
+
self.lora_path = (
|
|
177
|
+
None
|
|
178
|
+
if "generator_lora_path" not in config
|
|
179
|
+
else config["generator_lora_path"]
|
|
180
|
+
)
|
|
181
|
+
self.use_lora = False
|
|
182
|
+
if self.lora_path is not None:
|
|
183
|
+
self.use_lora = True
|
|
184
|
+
if self.use_lora:
|
|
185
|
+
self.model = LLM(
|
|
186
|
+
self.model_path,
|
|
187
|
+
tensor_parallel_size=tensor_parallel_size,
|
|
188
|
+
gpu_memory_utilization=gpu_memory_utilization,
|
|
189
|
+
enable_lora=True,
|
|
190
|
+
max_lora_rank=64,
|
|
191
|
+
max_logprobs=32016,
|
|
192
|
+
)
|
|
193
|
+
else:
|
|
194
|
+
self.model = LLM(
|
|
195
|
+
self.model_path,
|
|
196
|
+
tensor_parallel_size=tensor_parallel_size,
|
|
197
|
+
gpu_memory_utilization=gpu_memory_utilization,
|
|
198
|
+
max_logprobs=32016,
|
|
199
|
+
)
|
|
200
|
+
self.tokenizer = AutoTokenizer.from_pretrained(
|
|
201
|
+
self.model_path, trust_remote_code=True
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
@torch.inference_mode(mode=True)
|
|
205
|
+
def generate(
|
|
206
|
+
self,
|
|
207
|
+
input_list: List[str],
|
|
208
|
+
return_raw_output=False,
|
|
209
|
+
return_scores=False,
|
|
210
|
+
**params,
|
|
211
|
+
):
|
|
212
|
+
from vllm import SamplingParams
|
|
213
|
+
|
|
214
|
+
if isinstance(input_list, str):
|
|
215
|
+
input_list = [input_list]
|
|
216
|
+
|
|
217
|
+
generation_params = deepcopy(self.generation_params)
|
|
218
|
+
generation_params.update(params)
|
|
219
|
+
if "do_sample" in generation_params:
|
|
220
|
+
generation_params.pop("do_sample")
|
|
221
|
+
|
|
222
|
+
max_tokens = params.pop("max_tokens", None) or params.pop(
|
|
223
|
+
"max_new_tokens", None
|
|
224
|
+
)
|
|
225
|
+
if max_tokens is not None:
|
|
226
|
+
generation_params["max_tokens"] = max_tokens
|
|
227
|
+
else:
|
|
228
|
+
generation_params["max_tokens"] = generation_params.get(
|
|
229
|
+
"max_tokens", generation_params.pop("max_new_tokens", None)
|
|
230
|
+
)
|
|
231
|
+
generation_params.pop("max_new_tokens", None)
|
|
232
|
+
|
|
233
|
+
# fix for llama3
|
|
234
|
+
if "stop" in generation_params:
|
|
235
|
+
generation_params["stop"].append("<|eot_id|>")
|
|
236
|
+
else:
|
|
237
|
+
generation_params["stop"] = ["<|eot_id|>"]
|
|
238
|
+
|
|
239
|
+
if return_scores:
|
|
240
|
+
if "logprobs" not in generation_params:
|
|
241
|
+
generation_params["logprobs"] = 100
|
|
242
|
+
|
|
243
|
+
sampling_params = SamplingParams(**generation_params)
|
|
244
|
+
|
|
245
|
+
if self.use_lora:
|
|
246
|
+
from vllm.lora.request import LoRARequest
|
|
247
|
+
|
|
248
|
+
outputs = self.model.generate(
|
|
249
|
+
input_list,
|
|
250
|
+
sampling_params,
|
|
251
|
+
lora_request=LoRARequest("lora_module", 1, self.lora_path),
|
|
252
|
+
)
|
|
253
|
+
else:
|
|
254
|
+
outputs = self.model.generate(input_list, sampling_params)
|
|
255
|
+
|
|
256
|
+
if return_raw_output:
|
|
257
|
+
base_output = outputs
|
|
258
|
+
else:
|
|
259
|
+
generated_texts = [output.outputs[0].text for output in outputs]
|
|
260
|
+
base_output = generated_texts
|
|
261
|
+
if return_scores:
|
|
262
|
+
scores = []
|
|
263
|
+
for output in outputs:
|
|
264
|
+
logprobs = output.outputs[0].logprobs
|
|
265
|
+
scores.append(
|
|
266
|
+
[
|
|
267
|
+
np.exp(list(score_dict.values())[0].logprob)
|
|
268
|
+
for score_dict in logprobs
|
|
269
|
+
]
|
|
270
|
+
)
|
|
271
|
+
return base_output, scores
|
|
272
|
+
else:
|
|
273
|
+
return base_output
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
class HFCausalLMGenerator(BaseGenerator):
|
|
277
|
+
"""Class for decoder-only generator, based on hf."""
|
|
278
|
+
|
|
279
|
+
def __init__(self, config, model=None):
|
|
280
|
+
super().__init__(config)
|
|
281
|
+
self.config = config
|
|
282
|
+
lora_path = (
|
|
283
|
+
None
|
|
284
|
+
if "generator_lora_path" not in config
|
|
285
|
+
else config["generator_lora_path"]
|
|
286
|
+
)
|
|
287
|
+
self.model, self.tokenizer = self._load_model(model=model)
|
|
288
|
+
self.use_lora = False
|
|
289
|
+
if lora_path is not None:
|
|
290
|
+
self.use_lora = True
|
|
291
|
+
self.model.load_adapter(lora_path)
|
|
292
|
+
|
|
293
|
+
def _load_model(self, model=None):
|
|
294
|
+
r"""Load model and tokenizer for generator."""
|
|
295
|
+
if model is None:
|
|
296
|
+
model = AutoModelForCausalLM.from_pretrained(
|
|
297
|
+
self.model_path,
|
|
298
|
+
torch_dtype="auto",
|
|
299
|
+
device_map="auto",
|
|
300
|
+
trust_remote_code=True,
|
|
301
|
+
)
|
|
302
|
+
else:
|
|
303
|
+
model.cuda()
|
|
304
|
+
model.eval()
|
|
305
|
+
tokenizer = AutoTokenizer.from_pretrained(
|
|
306
|
+
self.model_path, trust_remote_code=True
|
|
307
|
+
)
|
|
308
|
+
if "qwen" not in self.model_name:
|
|
309
|
+
tokenizer.pad_token = tokenizer.eos_token
|
|
310
|
+
tokenizer.padding_side = "left"
|
|
311
|
+
|
|
312
|
+
return model, tokenizer
|
|
313
|
+
|
|
314
|
+
def add_new_tokens(
|
|
315
|
+
self, token_embedding_path, token_name_func=lambda idx: f"[ref{idx+1}]"
|
|
316
|
+
):
|
|
317
|
+
del self.model
|
|
318
|
+
self.model = AutoModelForCausalLM.from_pretrained(
|
|
319
|
+
self.model_path,
|
|
320
|
+
trust_remote_code=True,
|
|
321
|
+
)
|
|
322
|
+
# get original embedding weight matrix
|
|
323
|
+
embedding_layer = self.model.get_input_embeddings()
|
|
324
|
+
embedding_weights = embedding_layer.weight
|
|
325
|
+
original_vocab_size, embedding_dim = embedding_weights.shape
|
|
326
|
+
|
|
327
|
+
new_tokens_weights = torch.load(token_embedding_path)
|
|
328
|
+
new_tokens_length = new_tokens_weights.shape[0]
|
|
329
|
+
|
|
330
|
+
# expand vocabulary
|
|
331
|
+
new_tokens = [token_name_func(idx) for idx in range(new_tokens_length)]
|
|
332
|
+
self.tokenizer.add_tokens(new_tokens)
|
|
333
|
+
|
|
334
|
+
# create new embedding matrix
|
|
335
|
+
new_vocab_size = original_vocab_size + new_tokens_length
|
|
336
|
+
new_embedding_weights = torch.zeros(new_vocab_size, embedding_dim)
|
|
337
|
+
|
|
338
|
+
# copy original embeddings to the new weights
|
|
339
|
+
new_embedding_weights[:original_vocab_size, :] = embedding_weights
|
|
340
|
+
|
|
341
|
+
# append virtual token embeddings to the new weights
|
|
342
|
+
for token, embedding in zip(new_tokens, new_tokens_weights):
|
|
343
|
+
token_id = self.tokenizer.convert_tokens_to_ids(token)
|
|
344
|
+
new_embedding_weights[token_id] = embedding
|
|
345
|
+
|
|
346
|
+
# update the embedding table
|
|
347
|
+
# note: we should avoid using the function resize_token_embeddings() because this function will also change the lm_head of the model
|
|
348
|
+
embedding_layer.weight.data = new_embedding_weights
|
|
349
|
+
self.model.eval()
|
|
350
|
+
self.model.cuda()
|
|
351
|
+
|
|
352
|
+
@torch.inference_mode(mode=True)
|
|
353
|
+
def generate(
|
|
354
|
+
self,
|
|
355
|
+
input_list: List[str],
|
|
356
|
+
batch_size=None,
|
|
357
|
+
return_scores=False,
|
|
358
|
+
return_dict=False,
|
|
359
|
+
**params,
|
|
360
|
+
):
|
|
361
|
+
"""Generate batches one by one. The generated content needs to exclude input."""
|
|
362
|
+
|
|
363
|
+
if isinstance(input_list, str):
|
|
364
|
+
input_list = [input_list]
|
|
365
|
+
if batch_size is None:
|
|
366
|
+
batch_size = self.batch_size
|
|
367
|
+
|
|
368
|
+
generation_params = deepcopy(self.generation_params)
|
|
369
|
+
generation_params.update(params)
|
|
370
|
+
|
|
371
|
+
# deal stop params
|
|
372
|
+
stop_sym = None
|
|
373
|
+
if "stop" in generation_params:
|
|
374
|
+
from flashrag.generator.stop_word_criteria import StopWordCriteria
|
|
375
|
+
|
|
376
|
+
stop_sym = generation_params.pop("stop")
|
|
377
|
+
stopping_criteria = [
|
|
378
|
+
StopWordCriteria(
|
|
379
|
+
tokenizer=self.tokenizer,
|
|
380
|
+
prompts=input_list,
|
|
381
|
+
stop_words=stop_sym,
|
|
382
|
+
)
|
|
383
|
+
]
|
|
384
|
+
generation_params["stopping_criteria"] = stopping_criteria
|
|
385
|
+
|
|
386
|
+
max_tokens = params.pop("max_tokens", None) or params.pop(
|
|
387
|
+
"max_new_tokens", None
|
|
388
|
+
)
|
|
389
|
+
if max_tokens is not None:
|
|
390
|
+
generation_params["max_new_tokens"] = max_tokens
|
|
391
|
+
else:
|
|
392
|
+
generation_params["max_new_tokens"] = generation_params.get(
|
|
393
|
+
"max_new_tokens", generation_params.pop("max_tokens", None)
|
|
394
|
+
)
|
|
395
|
+
generation_params.pop("max_tokens", None)
|
|
396
|
+
|
|
397
|
+
# set eos token for llama
|
|
398
|
+
if "llama" in self.model_name.lower():
|
|
399
|
+
extra_eos_tokens = [
|
|
400
|
+
self.tokenizer.eos_token_id,
|
|
401
|
+
self.tokenizer.convert_tokens_to_ids("<|eot_id|>"),
|
|
402
|
+
]
|
|
403
|
+
if "eos_token_id" in generation_params:
|
|
404
|
+
generation_params["eos_token_id"].extend(extra_eos_tokens)
|
|
405
|
+
else:
|
|
406
|
+
generation_params["eos_token_id"] = extra_eos_tokens
|
|
407
|
+
|
|
408
|
+
responses = []
|
|
409
|
+
scores = []
|
|
410
|
+
generated_token_ids = []
|
|
411
|
+
generated_token_logits = []
|
|
412
|
+
|
|
413
|
+
for idx in trange(
|
|
414
|
+
0, len(input_list), batch_size, desc="Generation process: "
|
|
415
|
+
):
|
|
416
|
+
torch.cuda.empty_cache()
|
|
417
|
+
batched_prompts = input_list[idx : idx + batch_size]
|
|
418
|
+
inputs = self.tokenizer(
|
|
419
|
+
batched_prompts,
|
|
420
|
+
return_tensors="pt",
|
|
421
|
+
padding=True,
|
|
422
|
+
truncation=True,
|
|
423
|
+
max_length=self.max_input_len,
|
|
424
|
+
).to(self.model.device)
|
|
425
|
+
outputs = self.model.generate(
|
|
426
|
+
**inputs,
|
|
427
|
+
output_scores=True,
|
|
428
|
+
return_dict_in_generate=True,
|
|
429
|
+
**generation_params,
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
generated_ids = outputs.sequences
|
|
433
|
+
logits = torch.stack(outputs.scores, dim=1).softmax(-1)
|
|
434
|
+
generated_ids = generated_ids[:, inputs["input_ids"].shape[-1] :]
|
|
435
|
+
gen_score = (
|
|
436
|
+
torch.gather(logits, 2, generated_ids[:, :, None])
|
|
437
|
+
.squeeze(-1)
|
|
438
|
+
.cpu()
|
|
439
|
+
.tolist()
|
|
440
|
+
)
|
|
441
|
+
scores.extend(gen_score)
|
|
442
|
+
|
|
443
|
+
# get additinoal info
|
|
444
|
+
if return_dict:
|
|
445
|
+
batch_generated_token_ids = generated_ids.detach().cpu()
|
|
446
|
+
batch_generated_token_logits = (
|
|
447
|
+
torch.cat(
|
|
448
|
+
[
|
|
449
|
+
token_scores.unsqueeze(1)
|
|
450
|
+
for token_scores in outputs.scores
|
|
451
|
+
],
|
|
452
|
+
dim=1,
|
|
453
|
+
)
|
|
454
|
+
.detach()
|
|
455
|
+
.cpu()
|
|
456
|
+
)
|
|
457
|
+
if (
|
|
458
|
+
batch_generated_token_ids.shape[1]
|
|
459
|
+
< generation_params["max_new_tokens"]
|
|
460
|
+
):
|
|
461
|
+
real_batch_size, num_generated_tokens = (
|
|
462
|
+
batch_generated_token_ids.shape
|
|
463
|
+
)
|
|
464
|
+
padding_length = (
|
|
465
|
+
generation_params["max_new_tokens"]
|
|
466
|
+
- num_generated_tokens
|
|
467
|
+
)
|
|
468
|
+
padding_token_ids = torch.zeros(
|
|
469
|
+
(real_batch_size, padding_length),
|
|
470
|
+
dtype=batch_generated_token_ids.dtype,
|
|
471
|
+
).fill_(self.tokenizer.pad_token_id)
|
|
472
|
+
padding_token_logits = torch.zeros(
|
|
473
|
+
(
|
|
474
|
+
real_batch_size,
|
|
475
|
+
padding_length,
|
|
476
|
+
batch_generated_token_logits.shape[-1],
|
|
477
|
+
),
|
|
478
|
+
dtype=batch_generated_token_logits.dtype,
|
|
479
|
+
)
|
|
480
|
+
batch_generated_token_ids = torch.cat(
|
|
481
|
+
[batch_generated_token_ids, padding_token_ids], dim=1
|
|
482
|
+
)
|
|
483
|
+
batch_generated_token_logits = torch.cat(
|
|
484
|
+
[batch_generated_token_logits, padding_token_logits],
|
|
485
|
+
dim=1,
|
|
486
|
+
)
|
|
487
|
+
generated_token_ids.append(batch_generated_token_ids)
|
|
488
|
+
generated_token_logits.append(batch_generated_token_logits)
|
|
489
|
+
|
|
490
|
+
for i, generated_sequence in enumerate(outputs.sequences):
|
|
491
|
+
input_ids = inputs["input_ids"][i]
|
|
492
|
+
text = self.tokenizer.decode(
|
|
493
|
+
generated_sequence,
|
|
494
|
+
skip_special_tokens=True,
|
|
495
|
+
clean_up_tokenization_spaces=False,
|
|
496
|
+
)
|
|
497
|
+
if input_ids is None:
|
|
498
|
+
prompt_length = 0
|
|
499
|
+
else:
|
|
500
|
+
prompt_length = len(
|
|
501
|
+
self.tokenizer.decode(
|
|
502
|
+
input_ids,
|
|
503
|
+
skip_special_tokens=True,
|
|
504
|
+
clean_up_tokenization_spaces=False,
|
|
505
|
+
)
|
|
506
|
+
)
|
|
507
|
+
new_text = text[prompt_length:]
|
|
508
|
+
|
|
509
|
+
if stop_sym is not None:
|
|
510
|
+
strip_stopword = True
|
|
511
|
+
# Find the first occurrence of any stop word
|
|
512
|
+
lower_stop_index = len(new_text) # Default to end of text
|
|
513
|
+
for sym in stop_sym:
|
|
514
|
+
stop_index = new_text.find(sym)
|
|
515
|
+
if stop_index != -1:
|
|
516
|
+
# Adjust stop index based on whether we're stripping the stop word
|
|
517
|
+
stop_index += 0 if strip_stopword else len(sym)
|
|
518
|
+
lower_stop_index = min(stop_index, lower_stop_index)
|
|
519
|
+
|
|
520
|
+
# Cut the text at the first stop word found (if any)
|
|
521
|
+
new_text = new_text[:lower_stop_index]
|
|
522
|
+
|
|
523
|
+
responses.append(new_text.strip())
|
|
524
|
+
|
|
525
|
+
if return_dict:
|
|
526
|
+
generated_token_ids = torch.cat(generated_token_ids, dim=0)
|
|
527
|
+
generated_token_logits = torch.cat(generated_token_logits, dim=0)
|
|
528
|
+
return {
|
|
529
|
+
"generated_token_ids": generated_token_ids,
|
|
530
|
+
"generated_token_logits": generated_token_logits,
|
|
531
|
+
"responses": responses,
|
|
532
|
+
"scores": scores,
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if return_scores:
|
|
536
|
+
return responses, scores
|
|
537
|
+
else:
|
|
538
|
+
return responses
|
|
539
|
+
|
|
540
|
+
@torch.inference_mode(mode=True)
|
|
541
|
+
def cal_gen_probs(self, prev, next):
|
|
542
|
+
input_ids = self.tokenizer.encode(prev, add_special_tokens=False)
|
|
543
|
+
target_ids = self.tokenizer.encode(next, add_special_tokens=False)
|
|
544
|
+
context_ids = input_ids + target_ids
|
|
545
|
+
context_tensor = torch.tensor([context_ids]).to(self.device)
|
|
546
|
+
with torch.no_grad():
|
|
547
|
+
outputs = self.model(context_tensor)
|
|
548
|
+
logits = outputs.logits
|
|
549
|
+
logits = logits[0, len(input_ids) - 1 : len(context_ids) - 1, :]
|
|
550
|
+
logits = logits.to(torch.float32).detach().cpu()
|
|
551
|
+
# softmax to normalize
|
|
552
|
+
probs = torch.softmax(logits, dim=-1)
|
|
553
|
+
# obtain probs of target_ids
|
|
554
|
+
target_probs = probs[range(len(target_ids)), target_ids].numpy()
|
|
555
|
+
|
|
556
|
+
return logits, target_probs
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
class FastChatGenerator(HFCausalLMGenerator):
|
|
560
|
+
def __init__(self, config, model=None):
|
|
561
|
+
super().__init__(config)
|
|
562
|
+
|
|
563
|
+
def _load_model(self, model=None):
|
|
564
|
+
r"""Load model and tokenizer for generator."""
|
|
565
|
+
|
|
566
|
+
def get_gpu_memory(max_gpus=None):
|
|
567
|
+
"""Get available memory for each GPU."""
|
|
568
|
+
gpu_memory = []
|
|
569
|
+
num_gpus = (
|
|
570
|
+
torch.cuda.device_count()
|
|
571
|
+
if max_gpus is None
|
|
572
|
+
else min(max_gpus, torch.cuda.device_count())
|
|
573
|
+
)
|
|
574
|
+
for gpu_id in range(num_gpus):
|
|
575
|
+
with torch.cuda.device(gpu_id):
|
|
576
|
+
device = torch.cuda.current_device()
|
|
577
|
+
gpu_properties = torch.cuda.get_device_properties(device)
|
|
578
|
+
total_memory = gpu_properties.total_memory / (1024**3)
|
|
579
|
+
allocated_memory = torch.cuda.memory_allocated() / (1024**3)
|
|
580
|
+
available_memory = total_memory - allocated_memory
|
|
581
|
+
gpu_memory.append(available_memory)
|
|
582
|
+
return gpu_memory
|
|
583
|
+
|
|
584
|
+
if model is None:
|
|
585
|
+
from fastchat.model import load_model
|
|
586
|
+
|
|
587
|
+
if "gpu_memory_utilization" not in self.config:
|
|
588
|
+
gpu_memory_utilization = 0.85
|
|
589
|
+
else:
|
|
590
|
+
gpu_memory_utilization = self.config["gpu_memory_utilization"]
|
|
591
|
+
max_gpu_memory = None
|
|
592
|
+
if self.gpu_num != 1:
|
|
593
|
+
available_gpu_memory = get_gpu_memory(self.gpu_num)
|
|
594
|
+
max_gpu_memory = (
|
|
595
|
+
str(int(min(available_gpu_memory) * gpu_memory_utilization))
|
|
596
|
+
+ "GiB"
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
model, tokenizer = load_model(
|
|
600
|
+
self.model_path,
|
|
601
|
+
device="cuda",
|
|
602
|
+
num_gpus=self.gpu_num,
|
|
603
|
+
max_gpu_memory=max_gpu_memory,
|
|
604
|
+
load_8bit=False,
|
|
605
|
+
cpu_offloading=False,
|
|
606
|
+
debug=False,
|
|
607
|
+
)
|
|
608
|
+
|
|
609
|
+
else:
|
|
610
|
+
model.cuda()
|
|
611
|
+
tokenizer = AutoTokenizer.from_pretrained(
|
|
612
|
+
self.model_path, trust_remote_code=True
|
|
613
|
+
)
|
|
614
|
+
model.eval()
|
|
615
|
+
|
|
616
|
+
tokenizer = AutoTokenizer.from_pretrained(
|
|
617
|
+
self.model_path, trust_remote_code=True
|
|
618
|
+
)
|
|
619
|
+
if "qwen" not in self.model_name:
|
|
620
|
+
tokenizer.pad_token = tokenizer.eos_token
|
|
621
|
+
tokenizer.padding_side = "left"
|
|
622
|
+
|
|
623
|
+
return model, tokenizer
|