PowerMachine commited on
Commit
b12e951
·
verified ·
1 Parent(s): 52f0163

Delete data/streaming_dataset.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. data/streaming_dataset.py +0 -352
data/streaming_dataset.py DELETED
@@ -1,352 +0,0 @@
1
- """streaming_dataset.py — Carregador de dataset streaming para o CNN-BiGRU.
2
-
3
- Adaptado de xavante_work/flexnet/streaming_datasets_v13_9.py e do repositório
4
- 'PowerMachine/CNN-BiGRU' no HuggingFace, com:
5
- - Modo streaming (IterableDataset, sem materialização completa)
6
- - Suporte ao repositório próprio 'PowerMachine/CNN-BiGRU' (streaming_datasets.py)
7
- - Fallback sintético quando datasets externos estão indisponíveis
8
- - Suporte multimodal: texto + imagem (placeholder) + áudio (placeholder)
9
- - Garantia de produzir N amostras para o teste (até 500 samples em batches de 100)
10
-
11
- v3.0:
12
- - Prioriza o repositório 'PowerMachine/CNN-BiGRU' (conforme requisição do usuário)
13
- - Mantém fallback para os datasets V13.9.1 originais
14
- - Suporte a batches de 100 amostras (até 500 no total) para testes
15
- """
16
- from __future__ import annotations
17
-
18
- import logging
19
- import os
20
- import random
21
- from dataclasses import dataclass, field
22
- from typing import Any, Dict, Iterator, List, Optional, Sequence
23
-
24
- import numpy as np
25
- import torch
26
-
27
- logger = logging.getLogger(__name__)
28
-
29
-
30
- @dataclass
31
- class MultimodalSample:
32
- """Amostra multimodal: texto + imagem (HxWxC float) + áudio (spec TxF)."""
33
- sample_id: int
34
- text_a: str # Stream A (e.g. pergunta/título)
35
- text_b: str # Stream B (e.g. contexto/corpo)
36
- image: Optional[np.ndarray] = None # [H, W, C] float32 in [0,1]
37
- audio: Optional[np.ndarray] = None # [T, F] spectrogram float32
38
- label: int = 0
39
- metadata: Dict[str, Any] = field(default_factory=dict)
40
-
41
-
42
- # Dataset padrão V13.9.1 (mesmos do reference streaming_datasets_v13_9.py)
43
- # NOVO v3.0: prioriza o repositório próprio 'PowerMachine/CNN-BiGRU'
44
- # (conforme requisição do usuário: "usar do repositório 'PowerMachine/CNN-BiGRU' streaming_datasets.py")
45
- DEFAULT_DATASETS = [
46
- # Prioridade 1: repositório próprio do projeto
47
- "PowerMachine/CNN-BiGRU",
48
- # Prioridade 2: datasets V13.9.1 originais
49
- "CEIA-POSITIVO/ultrachat_br_clustred_balanced_v1",
50
- "Madras1/corpus-ptbr-v2",
51
- "rhaymison/multmodal_175k_portuguese",
52
- "TucanoBR/GigaVerbo",
53
- "nvidia/OpenMathReasoning",
54
- "MathLLMs/MathVision",
55
- "nvidia/OpenMathInstruct-2",
56
- "dominguesm/restore-punctuation-ptbr-dataset",
57
- "carolina-c4ai/corpus-carolina",
58
- ]
59
-
60
-
61
- def _extract_field(sample: Dict[str, Any], candidates: List[str]) -> Optional[str]:
62
- for f in candidates:
63
- if f in sample:
64
- v = sample[f]
65
- if isinstance(v, str) and v.strip():
66
- return v
67
- if isinstance(v, list):
68
- parts = []
69
- for m in v:
70
- if isinstance(m, dict):
71
- c = m.get("content", "")
72
- if isinstance(c, str) and c.strip():
73
- parts.append(c)
74
- elif isinstance(m, str) and m.strip():
75
- parts.append(m)
76
- if parts:
77
- return "\n".join(parts)
78
- return None
79
-
80
-
81
- def _try_load_hf_streaming(
82
- dataset_name: str,
83
- split: str = "train",
84
- hf_token: Optional[str] = None,
85
- ):
86
- """Tenta carregar dataset HF em streaming. Retorna None se falhar."""
87
- try:
88
- from datasets import load_dataset
89
- ds = load_dataset(dataset_name, split=split, streaming=True, token=hf_token)
90
- logger.info("Streaming OK: %s[%s]", dataset_name, split)
91
- return ds
92
- except Exception as e:
93
- logger.info("Streaming falhou para %s: %s", dataset_name, str(e)[:120])
94
- return None
95
-
96
-
97
- def synthetic_multimodal_stream(
98
- n_samples: int,
99
- seed: int = 42,
100
- image_size: tuple = (28, 28, 1),
101
- audio_shape: tuple = (64, 40),
102
- vocab_texts: Optional[Sequence[str]] = None,
103
- ) -> Iterator[MultimodalSample]:
104
- """Gera amostras sintéticas multimodais determinísticas.
105
-
106
- Usado quando o dataset HF está indisponível (offline, rate-limit, etc.).
107
- Garante que o teste de 50 amostras sempre execute.
108
- """
109
- rng = random.Random(seed)
110
- np_rng = np.random.RandomState(seed)
111
-
112
- if vocab_texts is None:
113
- vocab_texts = [
114
- "o modelo aprende padrões locais com convoluções",
115
- "a ponte de cooperação troca informações entre fluxos",
116
- "grus capturam dependências temporais bidirecionais",
117
- "atenção cruzada reduz perplexidade em textos longos",
118
- "a fusão multimodal combina texto imagem e áudio",
119
- "penalidades evitam repetições viciosas na geração",
120
- "a camada anti-alucinação usa lógica fuzzy de lukasiewicz",
121
- "o verificador classifica passos com sigmoid binária",
122
- "ajustes dinâmicos de lr controlam explosão de gradiente",
123
- "normalização espectral estabiliza o treinamento",
124
- ]
125
-
126
- n_vocab = len(vocab_texts)
127
- for i in range(n_samples):
128
- text_a = vocab_texts[rng.randrange(n_vocab)]
129
- text_b = vocab_texts[rng.randrange(n_vocab)]
130
- # Imagem sintética: padrões estruturados simples
131
- img = np_rng.rand(*image_size).astype(np.float32)
132
- # Adiciona um padrão que depende do índice (para discriminação)
133
- img[i % image_size[0], :, :] = 1.0
134
- # Áudio sintético: espectrograma
135
- aud = np_rng.rand(*audio_shape).astype(np.float32) * 0.5
136
- # Espectro com picos determinísticos
137
- aud[i % audio_shape[0], :] += 0.5
138
- yield MultimodalSample(
139
- sample_id=i,
140
- text_a=text_a,
141
- text_b=text_b,
142
- image=img,
143
- audio=aud,
144
- label=i % 3,
145
- metadata={"source": "synthetic", "idx": i},
146
- )
147
-
148
-
149
- class MultimodalStreamingDataset(torch.utils.data.IterableDataset):
150
- """Dataset streaming multimodal. Suporta HF + fallback sintético.
151
-
152
- Args:
153
- n_samples: número total de amostras a produzir.
154
- hf_datasets: lista de datasets HF para tentar (em ordem).
155
- hf_token: token HF (será limpo após uso).
156
- use_synthetic_fallback: se True, usa sintético quando HF falha.
157
- seed: seed para reprodutibilidade.
158
- """
159
-
160
- def __init__(
161
- self,
162
- n_samples: int = 50,
163
- hf_datasets: Optional[List[str]] = None,
164
- hf_token: Optional[str] = None,
165
- use_synthetic_fallback: bool = True,
166
- seed: int = 42,
167
- image_size: tuple = (28, 28, 1),
168
- audio_shape: tuple = (64, 40),
169
- ):
170
- super().__init__()
171
- self.n_samples = n_samples
172
- self.hf_datasets = hf_datasets or DEFAULT_DATASETS
173
- # NOTA: o hf_token NÃO é persistido como atributo de instância
174
- # para evitar que seja exposto em dumps/logs. Em vez disso, é
175
- # passado como parâmetro local durante a iteração.
176
- self._hf_token = hf_token # private, limpo após iter
177
- self.use_synthetic_fallback = use_synthetic_fallback
178
- self.seed = seed
179
- self.image_size = image_size
180
- self.audio_shape = audio_shape
181
-
182
- @property
183
- def hf_token(self) -> Optional[str]:
184
- """Retorna o token HF atual (ou None se já limpo)."""
185
- return getattr(self, "_hf_token", None)
186
-
187
- def clear_hf_token(self) -> None:
188
- """Limpa o token HF da memória da instância (boa prática de segurança)."""
189
- self._hf_token = None
190
-
191
- def _try_hf(self) -> Iterator[Dict[str, Any]]:
192
- """Tenta carregar amostras HF. Retorna iterator vazio se falhar."""
193
- for ds_name in self.hf_datasets:
194
- ds = _try_load_hf_streaming(ds_name, split="train", hf_token=self._hf_token)
195
- if ds is None:
196
- continue
197
- count = 0
198
- text_candidates = ["text", "content", "question", "problem", "input",
199
- "conversa", "description", "prompt", "instruction"]
200
- label_candidates = ["answer", "response", "output", "solution",
201
- "punctuated", "restored"]
202
- for raw in ds:
203
- if count >= self.n_samples:
204
- break
205
- try:
206
- text_a = _extract_field(raw, text_candidates) or ""
207
- text_b = _extract_field(raw, label_candidates) or ""
208
- if len(text_a) < 5:
209
- text_a = "pergunta de exemplo sobre o tema"
210
- if len(text_b) < 5:
211
- text_b = "resposta de exemplo para contexto"
212
- # Image/audio placeholder: geramos sintéticos para manter multimodal
213
- img = np.random.rand(*self.image_size).astype(np.float32) * 0.5
214
- aud = np.random.rand(*self.audio_shape).astype(np.float32) * 0.5
215
- yield {
216
- "sample_id": count,
217
- "text_a": text_a[:200], # truncado para teste
218
- "text_b": text_b[:200],
219
- "image": img,
220
- "audio": aud,
221
- "label": count % 3,
222
- "metadata": {"source": ds_name, "idx": count},
223
- }
224
- count += 1
225
- except Exception as e:
226
- logger.debug("skip sample %d from %s: %s", count, ds_name, e)
227
- continue
228
- if count > 0:
229
- logger.info("HF yield %d samples de %s", count, ds_name)
230
- return
231
-
232
- def __iter__(self) -> Iterator[Dict[str, Any]]:
233
- """Itera sobre as amostras: tenta HF primeiro, depois fallback sintético.
234
-
235
- CORREÇÃO v2.0: O bug original retornava imediatamente após `yield from
236
- self._try_hf()` mesmo se HF não tivesse produzido nenhuma amostra,
237
- tornando o fallback sintético INACESSÍVEL quando HF falhava silenciosamente.
238
- Agora rastreamos o número de amostras produzidas e fazemos fallback se zero.
239
- """
240
- n_yielded = 0
241
-
242
- # Tentar HF primeiro
243
- if self.hf_datasets:
244
- try:
245
- for sample in self._try_hf():
246
- yield sample
247
- n_yielded += 1
248
- if n_yielded >= self.n_samples:
249
- return
250
- except Exception as e:
251
- logger.warning(f"HF streaming falhou ({e}) — usando fallback sintético")
252
-
253
- # Se HF não produziu amostras suficientes, usar fallback sintético
254
- if n_yielded < self.n_samples and self.use_synthetic_fallback:
255
- if n_yielded == 0:
256
- logger.info("Nenhuma amostra HF produzida — usando 100%% sintético")
257
- else:
258
- logger.info(f"HF produziu apenas {n_yielded}/{self.n_samples} — completando com sintético")
259
- remaining = self.n_samples - n_yielded
260
- for s in synthetic_multimodal_stream(
261
- n_samples=remaining,
262
- seed=self.seed + n_yielded, # seed diferente para variar
263
- image_size=self.image_size,
264
- audio_shape=self.audio_shape,
265
- ):
266
- yield {
267
- "sample_id": s.sample_id + n_yielded, # offset para não colidir
268
- "text_a": s.text_a,
269
- "text_b": s.text_b,
270
- "image": s.image,
271
- "audio": s.audio,
272
- "label": s.label,
273
- "metadata": {**s.metadata, "source": "synthetic_fallback"},
274
- }
275
- n_yielded += 1
276
- if n_yielded >= self.n_samples:
277
- # Limpa o token HF após uso completo (boa prática de segurança)
278
- self.clear_hf_token()
279
- return
280
- # Limpa o token HF após uso completo (boa prática de segurança)
281
- self.clear_hf_token()
282
-
283
-
284
- def collate_multimodal(
285
- batch: List[Dict[str, Any]],
286
- tokenizer,
287
- max_len: int = 64,
288
- ) -> Dict[str, torch.Tensor]:
289
- """Cola um batch de amostras multimodais em tensores.
290
-
291
- Returns dict com:
292
- input_ids_a: [B, T] (stream A)
293
- input_ids_b: [B, T] (stream B)
294
- attn_mask_a: [B, T]
295
- attn_mask_b: [B, T]
296
- images: [B, C, H, W]
297
- audios: [B, 1, T, F]
298
- labels: [B]
299
- """
300
- texts_a = [b["text_a"] for b in batch]
301
- texts_b = [b["text_b"] for b in batch]
302
- ids_a = tokenizer.encode_batch(texts_a, add_special=True)
303
- ids_b = tokenizer.encode_batch(texts_b, add_special=True)
304
-
305
- pad_id = tokenizer.pad_id
306
-
307
- def _pad(seqs, max_len):
308
- out = []
309
- masks = []
310
- for s in seqs:
311
- s = s[:max_len]
312
- n = len(s)
313
- padded = s + [pad_id] * (max_len - n)
314
- mask = [1] * n + [0] * (max_len - n)
315
- out.append(padded)
316
- masks.append(mask)
317
- return out, masks
318
-
319
- padded_a, masks_a = _pad(ids_a, max_len)
320
- padded_b, masks_b = _pad(ids_b, max_len)
321
-
322
- images = np.stack([b["image"] for b in batch]) # [B, H, W, C]
323
- # converte para [B, C, H, W]
324
- if images.ndim == 4:
325
- images = np.transpose(images, (0, 3, 1, 2))
326
- else:
327
- images = images[:, None, :, :] # add channel dim
328
-
329
- audios = np.stack([b["audio"] for b in batch]) # [B, T, F]
330
- audios = audios[:, None, :, :] # [B, 1, T, F]
331
-
332
- labels = np.array([b["label"] for b in batch], dtype=np.int64)
333
-
334
- return {
335
- "input_ids_a": torch.tensor(padded_a, dtype=torch.long),
336
- "input_ids_b": torch.tensor(padded_b, dtype=torch.long),
337
- "attn_mask_a": torch.tensor(masks_a, dtype=torch.float),
338
- "attn_mask_b": torch.tensor(masks_b, dtype=torch.float),
339
- "images": torch.tensor(images, dtype=torch.float32),
340
- "audios": torch.tensor(audios, dtype=torch.float32),
341
- "labels": torch.tensor(labels, dtype=torch.long),
342
- "sample_ids": [b["sample_id"] for b in batch],
343
- }
344
-
345
-
346
- __all__ = [
347
- "MultimodalSample",
348
- "MultimodalStreamingDataset",
349
- "collate_multimodal",
350
- "DEFAULT_DATASETS",
351
- "synthetic_multimodal_stream",
352
- ]