Files changed (1) hide show
  1. README.md +337 -0
README.md ADDED
@@ -0,0 +1,337 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ base_model:
3
+ - meta-llama/Llama-3.2-1B
4
+ ---
5
+
6
+ # Orion Quality Scorer
7
+
8
+ Built to classify datasets by Score Quality from 1 (worst quality) to 6 (best quality)
9
+
10
+ Usage code
11
+ ```python
12
+ #!/usr/bin/env python3
13
+ import sys
14
+ import os
15
+
16
+ # Passo 1: Parsear os argumentos de linha de comando antecipadamente para definir variáveis de ambiente
17
+ # Antes de importar transformers, parseamos os argumentos que podem afetar a configuração do modelo
18
+ def early_parse_args():
19
+ import argparse
20
+
21
+ parser = argparse.ArgumentParser(description="Scorer de Qualidade para Datasets do Hugging Face (Early Parsing)")
22
+ parser.add_argument(
23
+ "--4bit",
24
+ action="store_true",
25
+ dest="fourbit",
26
+ help="Carrega o modelo em precisão de 4 bits utilizando BitsAndBytes."
27
+ )
28
+ # Parse apenas os argumentos necessários para BitsAndBytes
29
+ args, _ = parser.parse_known_args()
30
+ return args
31
+
32
+ early_args = early_parse_args()
33
+
34
+ # Definir a variável de ambiente antes de importar transformers, se --4bit for usado
35
+ if early_args.fourbit:
36
+ os.environ["LLM_INT8_ENABLE_FP32_CPU_OFFLOAD"] = "true"
37
+
38
+ # Passo 2: Importar as bibliotecas restantes após definir variáveis de ambiente
39
+ import argparse
40
+ import json
41
+ from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
42
+ import numpy as np
43
+ from scipy.special import softmax
44
+ from datasets import load_dataset
45
+ import pyarrow.parquet as pq
46
+ from tqdm import tqdm
47
+ import torch
48
+
49
+ def infer_quality(model, tokenizer, input_text, resp_text, device):
50
+ """
51
+ Calcula a pontuação de qualidade para uma resposta baseada na entrada e na resposta fornecida.
52
+
53
+ Args:
54
+ model: O modelo de linguagem causal carregado.
55
+ tokenizer: O tokenizer correspondente ao modelo.
56
+ input_text (str): O texto da instrução/pergunta.
57
+ resp_text (str): O texto da resposta a ser avaliada.
58
+ device: O dispositivo (CPU ou GPU) para realizar os cálculos.
59
+
60
+ Returns:
61
+ float: A pontuação de qualidade calculada.
62
+ """
63
+ # Template traduzido para português
64
+ quality_template = (
65
+ "Você é um assistente útil. Por favor, identifique a pontuação de qualidade da Resposta correspondente à Pergunta.\n"
66
+ "#Pergunta#:\n{instruction}\n"
67
+ "#Resposta#:\n{output}\n"
68
+ "##Qualidade: "
69
+ )
70
+
71
+ # Formatar o input do usuário
72
+ user_input = quality_template.format(instruction=input_text, output=resp_text)
73
+
74
+ # Tokenizar a entrada
75
+ input_ids = tokenizer.encode(user_input, return_tensors="pt").to(device)
76
+ max_length = 512 # Definir comprimento máximo
77
+
78
+ # Gerar a resposta do modelo
79
+ with torch.no_grad():
80
+ outputs = model.generate(
81
+ input_ids,
82
+ max_length=max_length,
83
+ num_return_sequences=1,
84
+ return_dict_in_generate=True,
85
+ output_scores=True,
86
+ do_sample=False # Desativar amostragem para obter logits determinísticos
87
+ )
88
+
89
+ # Extrair os scores das saídas
90
+ scores = outputs.scores # Tuple of tensors, one for each generated token
91
+ score_logits = []
92
+
93
+ # Mapeamento de IDs de tokens para pontuações
94
+ id2score = {
95
+ 29896: "1", # Token ID para "1"
96
+ 29906: "2", # Token ID para "2"
97
+ 29941: "3", # Token ID para "3"
98
+ 29946: "4", # Token ID para "4"
99
+ 29945: "5", # Token ID para "5"
100
+ 29953: "6" # Token ID para "6"
101
+ }
102
+
103
+ score_template = np.array([1, 2, 3, 4, 5, 6])
104
+
105
+ # Verificar se há scores gerados
106
+ if not scores:
107
+ return 0.0 # Retorna 0 se não houver scores
108
+
109
+ # Assumindo que queremos a última posição gerada
110
+ last_score = scores[-1] # Último conjunto de logits, shape: (batch_size * num_beams, vocab_size)
111
+
112
+ # Garantir que last_score tenha o formato esperado
113
+ if last_score.ndim != 2 or last_score.size(0) != 1:
114
+ print("Formato inesperado para 'last_score'.")
115
+ return 0.0
116
+
117
+ last_score = last_score[0] # Shape: (vocab_size,)
118
+
119
+ # Extrair logits para os token_ids específicos
120
+ for token_id in id2score:
121
+ if token_id < last_score.size(0):
122
+ logit = last_score[token_id].item()
123
+ score_logits.append(logit)
124
+ else:
125
+ # Se o token_id estiver fora do alcance, atribuir um valor muito baixo
126
+ score_logits.append(-1e10)
127
+
128
+ score_logits = np.array(score_logits)
129
+
130
+ # Aplicar softmax para obter probabilidades
131
+ score_probs = softmax(score_logits)
132
+
133
+ # Calcular a pontuação ponderada
134
+ quality_score = np.sum(score_probs * score_template)
135
+
136
+ return quality_score
137
+
138
+ def process_dataset(model, tokenizer, dataset_repo, input_field, output_field, output_dir, device):
139
+ """
140
+ Processa um dataset específico, calculando a pontuação de qualidade para cada exemplo e salvando o resultado.
141
+
142
+ Args:
143
+ model: O modelo de linguagem causal carregado.
144
+ tokenizer: O tokenizer correspondente ao modelo.
145
+ dataset_repo (str): O repositório do dataset no Hugging Face.
146
+ input_field (str): O nome do campo de entrada no dataset.
147
+ output_field (str): O nome do campo de resposta no dataset.
148
+ output_dir (str): O diretório onde os datasets processados serão salvos.
149
+ device: O dispositivo (CPU ou GPU) para realizar os cálculos.
150
+ """
151
+ print(f"\nProcessando dataset: {dataset_repo}")
152
+ try:
153
+ dataset = load_dataset(dataset_repo)
154
+ except Exception as e:
155
+ print(f"Erro ao carregar o dataset {dataset_repo}: {e}")
156
+ return
157
+
158
+ # Considerar apenas splits que contêm dados (por exemplo, 'train', 'validation', 'test')
159
+ for split in dataset.keys():
160
+ split_data = dataset[split]
161
+ if len(split_data) == 0:
162
+ continue
163
+
164
+ print(f" Processando split: {split} com {len(split_data)} exemplos")
165
+
166
+ # Preparar listas para armazenar os scores
167
+ quality_scores = []
168
+
169
+ # Iterar sobre os exemplos com tqdm para visualizar o progresso
170
+ for example in tqdm(split_data, desc=f" Avaliando {split}"):
171
+ input_text = example.get(input_field, "")
172
+ output_text = example.get(output_field, "")
173
+ if not isinstance(input_text, str) or not isinstance(output_text, str):
174
+ quality_scores.append(0.0)
175
+ continue
176
+ score = infer_quality(model, tokenizer, input_text, output_text, device)
177
+ quality_scores.append(score)
178
+
179
+ # Adicionar a nova coluna ao dataset
180
+ split_data = split_data.add_column("quality_score", quality_scores)
181
+
182
+ # Definir o caminho de salvamento
183
+ dataset_name = dataset_repo.split('/')[-1]
184
+ split_output_dir = os.path.join(output_dir, dataset_name)
185
+ os.makedirs(split_output_dir, exist_ok=True)
186
+ output_path = os.path.join(split_output_dir, f"{split}.parquet")
187
+
188
+ # Salvar o dataset como .parquet
189
+ try:
190
+ split_data.to_parquet(output_path)
191
+ print(f" Salvado em {output_path}")
192
+ except Exception as e:
193
+ print(f" Erro ao salvar {output_path}: {e}")
194
+
195
+ def verify_token_ids(tokenizer, id2score):
196
+ """
197
+ Verifica se os token_ids mapeados correspondem aos tokens corretos.
198
+
199
+ Args:
200
+ tokenizer: O tokenizer correspondente ao modelo.
201
+ id2score (dict): Mapeamento de token_id para pontuação.
202
+ """
203
+ print("Verificando mapeamento de token IDs:")
204
+ for token_id, label in id2score.items():
205
+ try:
206
+ token = tokenizer.convert_ids_to_tokens(token_id)
207
+ print(f"Token ID {token_id}: '{token}' -> {label}")
208
+ except Exception as e:
209
+ print(f"Erro ao converter token_id {token_id}: {e}")
210
+
211
+ def main():
212
+ parser = argparse.ArgumentParser(description="Scorer de Qualidade para Datasets do Hugging Face")
213
+ parser.add_argument(
214
+ "--datasets",
215
+ type=str,
216
+ required=True,
217
+ help='Lista de datasets no formato JSON. Exemplo: \'[{{ "repo": "orion-research/gsmqnaoa-pt_BR", "input": "INSTRUCTION", "output": "RESPONSE" }}, {{ "repo": "orion-research/Aura-CoT-Multilang-v1", "input": "input", "output": "output" }}]\''
218
+ )
219
+ parser.add_argument(
220
+ "--output",
221
+ type=str,
222
+ required=True,
223
+ help="Diretório de saída onde os datasets processados serão salvos."
224
+ )
225
+ parser.add_argument(
226
+ "--cpu",
227
+ action="store_true",
228
+ help="Força o uso da CPU em vez da GPU."
229
+ )
230
+ parser.add_argument(
231
+ "--4bit",
232
+ action="store_true",
233
+ dest="fourbit",
234
+ help="Carrega o modelo em precisão de 4 bits utilizando BitsAndBytes."
235
+ )
236
+
237
+ args = parser.parse_args()
238
+
239
+ # Parsear o argumento datasets
240
+ try:
241
+ datasets_list = json.loads(args.datasets)
242
+ if not isinstance(datasets_list, list):
243
+ raise ValueError("O argumento --datasets deve ser uma lista de objetos JSON.")
244
+ except json.JSONDecodeError as e:
245
+ print(f"Erro ao parsear o argumento --datasets: {e}")
246
+ sys.exit(1)
247
+ except ValueError as ve:
248
+ print(ve)
249
+ sys.exit(1)
250
+
251
+ # Verificar se o diretório de saída existe, senão criar
252
+ if not os.path.exists(args.output):
253
+ try:
254
+ os.makedirs(args.output)
255
+ print(f"Criado diretório de saída: {args.output}")
256
+ except Exception as e:
257
+ print(f"Erro ao criar o diretório de saída {args.output}: {e}")
258
+ sys.exit(1)
259
+
260
+ # Determinar o dispositivo a ser usado
261
+ if args.cpu:
262
+ device = torch.device("cpu")
263
+ print("Forçando o uso da CPU.")
264
+ else:
265
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
266
+ print(f"Usando dispositivo: {device}")
267
+
268
+ # Carregar modelo e tokenizer
269
+ model_name = "/ors/models/LLM/Orion-Quality-Scorer"
270
+ print("Carregando tokenizer e modelo...")
271
+ try:
272
+ if args.fourbit:
273
+ # Verificar se bitsandbytes está instalado
274
+ try:
275
+ import bitsandbytes as bnb
276
+ except ImportError:
277
+ print("Erro: bitsandbytes não está instalado. Instale com 'pip install bitsandbytes'.")
278
+ sys.exit(1)
279
+
280
+ from transformers import BitsAndBytesConfig
281
+
282
+ # Configuração para 4-bit
283
+ bnb_config = BitsAndBytesConfig(
284
+ load_in_4bit=True,
285
+ bnb_4bit_use_double_quant=True,
286
+ bnb_4bit_quant_type="nf4",
287
+ bnb_4bit_compute_dtype=torch.float16
288
+ )
289
+
290
+ # Definir o device_map para permitir offloading
291
+ device_map = "auto" if not args.cpu else {"": "cpu"}
292
+
293
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
294
+ model = AutoModelForCausalLM.from_pretrained(
295
+ model_name,
296
+ quantization_config=bnb_config,
297
+ device_map=device_map,
298
+ trust_remote_code=True # Se o modelo requer código remoto
299
+ )
300
+ else:
301
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
302
+ model = AutoModelForCausalLM.from_pretrained(model_name)
303
+ model.to(device)
304
+
305
+ model.eval()
306
+ print("Modelo e tokenizer carregados com sucesso.")
307
+
308
+ # Verificar mapeamento de token IDs
309
+ verify_token_ids(tokenizer, {
310
+ 29896: "1",
311
+ 29906: "2",
312
+ 29941: "3",
313
+ 29946: "4",
314
+ 29945: "5",
315
+ 29953: "6"
316
+ })
317
+ except Exception as e:
318
+ print(f"Erro ao carregar o modelo ou tokenizer: {e}")
319
+ sys.exit(1)
320
+
321
+ # Processar cada dataset
322
+ for dataset_info in datasets_list:
323
+ repo = dataset_info.get("repo")
324
+ input_field = dataset_info.get("input")
325
+ output_field = dataset_info.get("output")
326
+
327
+ if not repo or not input_field or not output_field:
328
+ print(f"Informações incompletas para o dataset: {dataset_info}. Pulando...")
329
+ continue
330
+
331
+ process_dataset(model, tokenizer, repo, input_field, output_field, args.output, device)
332
+
333
+ print("\nProcessamento concluído.")
334
+
335
+ if __name__ == "__main__":
336
+ main()
337
+ ```