V6.7: upload src/bigru_t/data/streaming_datasets.py (37.9KB) β FASE1+FASE2 training results
e057fbc verified Download src/bigru_t/data/streaming_datasets.py from PowerMachine/BiGRU_T_version: direct link, hf CLI and curl.
- Browser
- Download file 38.8 kB
-
https://huggingface.co/PowerMachine/BiGRU_T_version/resolve/main/src/bigru_t/data/streaming_datasets.py
- Command line
-
hf download hf://PowerMachine/BiGRU_T_version/src/bigru_t/data/streaming_datasets.py
-
curl -L -o streaming_datasets.py https://huggingface.co/PowerMachine/BiGRU_T_version/resolve/main/src/bigru_t/data/streaming_datasets.py
38.8 kB
| """streaming_datasets_v13_9.py β Streaming dataset loader for v13.9.1 training. | |
| Carrega os 9 datasets especificados pelo usuΓ‘rio via streaming (IterableDataset), | |
| um por vez, exaustivamente dentro do limite max_samples_per_dataset. | |
| Datasets: | |
| 1. CEIA-POSITIVO/ultrachat_br_clustred_balanced_v1 | |
| 2. Madras1/corpus-ptbr-v2 | |
| 3. rhaymison/multmodal_175k_portuguese | |
| 4. TucanoBR/GigaVerbo | |
| 5. nvidia/OpenMathReasoning | |
| 6. MathLLMs/MathVision | |
| 7. nvidia/OpenMathInstruct-2 | |
| 8. dominguesm/restore-punctuation-ptbr-dataset | |
| 9. carolina-c4ai/corpus-carolina | |
| Memory-efficient: streaming=True, no full materialization. | |
| Returns ProcessedSample with raw_text only (tokenizer handles encoding). | |
| """ | |
| from __future__ import annotations | |
| import logging | |
| import os | |
| import time | |
| import traceback | |
| from dataclasses import dataclass, field | |
| from typing import Any, Dict, Iterator, List, Optional, Tuple | |
| import torch | |
| logger = logging.getLogger(__name__) | |
| # Datasets V13.9.1 β exatamente os 9 especificados pelo usuΓ‘rio | |
| # V6.5-final: adicionado dominguesm/Canarim-Instruct-PTBR-Dataset | |
| # V6.5-7ds: adicionados adalbertojunior/punctuation-ptbr, | |
| # iara-project/news-articles-ptbr-dataset, manoela/noticias_ptbr | |
| DEFAULT_DATASETS = [ | |
| "CEIA-POSITIVO/ultrachat_br_clustred_balanced_v1", | |
| "Madras1/corpus-ptbr-v2", | |
| "rhaymison/multmodal_175k_portuguese", | |
| "TucanoBR/GigaVerbo", | |
| "nvidia/OpenMathReasoning", | |
| "MathLLMs/MathVision", | |
| "nvidia/OpenMathInstruct-2", | |
| "dominguesm/restore-punctuation-ptbr-dataset", | |
| "carolina-c4ai/corpus-carolina", | |
| "dominguesm/Canarim-Instruct-PTBR-Dataset", | |
| "adalbertojunior/punctuation-ptbr", | |
| "iara-project/news-articles-ptbr-dataset", | |
| "manoela/noticias_ptbr", | |
| ] | |
| # Dataset format descriptors β Lista priorizada de campos de texto | |
| DATASET_FORMATS: Dict[str, Dict[str, Any]] = { | |
| "CEIA-POSITIVO/ultrachat_br_clustred_balanced_v1": { | |
| "type": "chat", | |
| "text_fields": ["conversa", "text", "content", "conversation", "messages"], | |
| "label_fields": [], | |
| "split": "train", | |
| "config": None, | |
| }, | |
| "Madras1/corpus-ptbr-v2": { | |
| "type": "text", | |
| "text_fields": ["text", "content", "document", "body"], | |
| "label_fields": [], | |
| "split": "train", | |
| "config": None, | |
| }, | |
| "rhaymison/multmodal_175k_portuguese": { | |
| "type": "multimodal", | |
| "text_fields": ["description", "text", "question", "prompt", "instruction", "input"], | |
| "image_fields": ["image", "image_url"], | |
| "label_fields": ["answer", "response", "output"], | |
| "split": "train", | |
| "config": None, | |
| }, | |
| "TucanoBR/GigaVerbo": { | |
| "type": "text", | |
| "text_fields": ["text", "content", "document", "body"], | |
| "label_fields": [], | |
| "split": "train", | |
| "config": None, | |
| }, | |
| "nvidia/OpenMathReasoning": { | |
| "type": "math", | |
| "text_fields": ["problem", "question", "input", "prompt"], | |
| "label_fields": ["solution", "answer", "output", "response"], | |
| # BUG FIX: OpenMathReasoning has splits ['cot', 'tir', 'genselect', 'additional_problems'] | |
| "split": "cot", # Use 'cot' (chain-of-thought) as primary split | |
| "config": None, | |
| }, | |
| "MathLLMs/MathVision": { | |
| "type": "math_multimodal", | |
| "text_fields": ["question", "problem", "text", "query"], | |
| "image_fields": ["image", "image_url"], | |
| "label_fields": ["answer", "solution", "response"], | |
| # BUG FIX: MathVision has only 'test' and 'testmini' splits (no train) | |
| "split": "test", | |
| "config": None, | |
| }, | |
| "nvidia/OpenMathInstruct-2": { | |
| "type": "math", | |
| "text_fields": ["problem", "question", "input", "prompt"], | |
| # V3 FIX: campos reais do dataset sΓ£o generated_solution e expected_answer | |
| "label_fields": ["generated_solution", "expected_answer", "solution", "answer", "output", "response"], | |
| "split": "train", | |
| "config": None, | |
| }, | |
| "dominguesm/restore-punctuation-ptbr-dataset": { | |
| "type": "punctuation", | |
| "text_fields": ["text", "original", "unpunctuated", "input"], | |
| "label_fields": ["punctuated", "restored", "target", "output"], | |
| "split": "train", | |
| "config": None, | |
| }, | |
| "carolina-c4ai/corpus-carolina": { | |
| "type": "text", | |
| "text_fields": ["text", "content", "document", "body", "xml"], | |
| "label_fields": [], | |
| "split": "train", | |
| "config": None, | |
| # V13.9.2-carolina: dataset original usa script Python (nΓ£o suportado em | |
| # datasets 5.0+). Workaround: stream_xml_gz direto via lxml.iterparse, | |
| # sem usar o script Python. Implementado em _stream_carolina_direct(). | |
| }, | |
| # ββ V13.9.2-finetune-v2: novos datasets PT-BR βββββββββββββββββββββββββββ | |
| # Adicionados para fine-tuning contΓnuo do modelo v13.9.2 (preserva | |
| # arquitetura e parΓ’metros). Nenhuma mudanΓ§a estrutural β apenas novos | |
| # descritores de formato para que stream_dataset() reconheΓ§a os datasets. | |
| # | |
| # V13.9.2-finetune-v3: padronizaΓ§Γ£o para o formato unificado | |
| # "### Instruction:\n...\n\n### Response:\n..." conforme requisitado pelo | |
| # usuΓ‘rio. Apenas os datasets com estrutura instruction/response (orion, | |
| # cnmoro) usam o template; strak2005/bratao continuam como texto puro | |
| # (corpus sem estrutura de instruΓ§Γ£o). Nenhuma mudanΓ§a nos 9 datasets | |
| # base β retrocompatibilidade total. | |
| "orion-research/translations-en_US-pt_BR": { | |
| "type": "translation", | |
| # string = EN (instruction), string_translation = PT-BR (response). | |
| # V13.9.2-finetune-v3: separa instruction/response para o template | |
| # unificado em vez de concatenar como texto ΓΊnico. | |
| "text_fields": ["string"], | |
| "label_fields": ["string_translation"], | |
| "split": "train", | |
| "config": None, | |
| "format_template": "instruction_response", | |
| "instruction_prefix": "Translate the following text to Portuguese:", | |
| }, | |
| "cnmoro/Instruct-PTBR-10M": { | |
| "type": "instruct", | |
| # NB: campos em MAIΓSCULAS no schema do dataset | |
| # V13.9.2-finetune-v3: INSTRUCTION -> instruction, RESPONSE -> response | |
| # via template unificado. | |
| "text_fields": ["INSTRUCTION"], | |
| "label_fields": ["RESPONSE"], | |
| "split": "train", | |
| "config": None, | |
| "format_template": "instruction_response", | |
| # V13.9.2-finetune-v2: dataset Γ© um ΓΊnico parquet de 10 GB β streaming | |
| # via datasets.load_dataset() Γ© extremamente lento para materializar | |
| # a primeira amostra. Fallback via HF datasets-server rows API (HTTP), | |
| # que retorna batches de 100 rows rapidamente. Implementado em | |
| # _stream_via_rows_api(). | |
| "loader": "rows_api", | |
| }, | |
| "strak2005/corpus-ptbr-v1": { | |
| "type": "text", | |
| "text_fields": ["text", "content"], | |
| "label_fields": [], | |
| "split": "train", | |
| "config": None, | |
| # Fallback: se strak2005 cair, tenta bratao (mirror idΓͺntico) | |
| "fallback": "bratao/corpus-ptbr-v1", | |
| # V13.9.2-finetune-v2: dataset tem 18 shards parquet de ~1GB cada. | |
| # Streaming via datasets.load_dataset() nΓ£o materializa primeira | |
| # amostra em tempo aceitΓ‘vel. Fallback: baixa 1 shard localmente e | |
| # itera com pyarrow. Implementado em _stream_via_parquet_first_shard(). | |
| "loader": "parquet_first_shard", | |
| }, | |
| "bratao/corpus-ptbr-v1": { | |
| "type": "text", | |
| "text_fields": ["text", "content"], | |
| "label_fields": [], | |
| "split": "train", | |
| "config": None, | |
| "loader": "parquet_first_shard", | |
| }, | |
| # V6.7 β BrunoN-Dev/corpus-ptbr-v1 (user requirement: FASE2 com puniΓ§Γ£o | |
| # ativa para 'BrunoN-Dev/corpus-ptbr-v1'). Dataset usa 18 parquet shards | |
| # grandes (~1GB total), entΓ£o usamos parquet_first_shard loader (igual ao | |
| # bratao/corpus-ptbr-v1). | |
| "BrunoN-Dev/corpus-ptbr-v1": { | |
| "type": "text", | |
| "text_fields": ["text", "content"], | |
| "label_fields": [], | |
| "split": "train", | |
| "config": None, | |
| "loader": "parquet_first_shard", | |
| }, | |
| # ββ V6.5: novos datasets para esgotar ββββββββββββββββββββββββββββββββ | |
| # User requirement: "esgotar 'dominguesm/restore-punctuation-ptbr-dataset' | |
| # e 'carolina-c4ai/corpus-carolina' e 'nvidia/OpenMathInstruct-2' e | |
| # 'nvidia/OpenMathReasoning' e 'CEIA-POSITIVO/ultrachat_br_clustred_balanced_v1' | |
| # e 'Dexavator/English-PTBR'" | |
| # | |
| # Dexavator/English-PTBR: dataset de traduΓ§Γ£o EN->PT-BR. | |
| # Estrutura tΓpica: {"english": "...", "portuguese": "..."} ou | |
| # {"en": "...", "pt": "..."}. | |
| "Dexavator/English-PTBR": { | |
| "type": "translation", | |
| "text_fields": ["english", "en", "text", "source"], | |
| "label_fields": ["portuguese", "pt", "target", "translation"], | |
| "split": "train", | |
| "config": None, | |
| "format_template": "instruction_response", | |
| "instruction_prefix": "Translate the following text to Portuguese:", | |
| }, | |
| # ββ V6.5-final: Canarim-Instruct-PTBR-Dataset ββββββββββββββββββββββββ | |
| # User requirement: "esgotar 'dominguesm/Canarim-Instruct-PTBR-Dataset'" | |
| # Canarim-Instruct: dataset PT-BR de instruΓ§Γ΅es (~430k samples). | |
| # Estrutura tΓpica: {"instruction": "...", "input": "...", "output": "..."} | |
| # ou {"text": "...", "conversation": [{"role": "user", "content": "..."}, ...]} | |
| "dominguesm/Canarim-Instruct-PTBR-Dataset": { | |
| "type": "instruct", | |
| "text_fields": ["instruction", "input", "text", "prompt", "question"], | |
| "label_fields": ["output", "response", "answer"], | |
| "split": "train", | |
| "config": None, | |
| "format_template": "instruction_response", | |
| "instruction_prefix": "InstruΓ§Γ£o:", | |
| }, | |
| # ββ V6.5-7ds: novos 3 datasets PT-BR βββββββββββββββββββββββββββββββββ | |
| # User requirement (latest): "streaming atΓ© esgotar nesta sequΓͺncia | |
| # 'dominguesm/restore-punctuation-ptbr-dataset' e | |
| # 'carolina-c4ai/corpus-carolina' e | |
| # 'CEIA-POSITIVO/ultrachat_br_clustred_balanced_v1' e | |
| # 'dominguesm/Canarim-Instruct-PTBR-Dataset' e | |
| # 'adalbertojunior/punctuation-ptbr' e | |
| # 'iara-project/news-articles-ptbr-dataset' e | |
| # 'manoela/noticias_ptbr'" | |
| # | |
| # adalbertojunior/punctuation-ptbr: | |
| # Dataset de restauraΓ§Γ£o de pontuaΓ§Γ£o PT-BR. Schema: | |
| # {id: str, tokens: list[str], pos_tags, chunk_tags, ner_tags} | |
| # O campo "tokens" Γ© uma lista de palavras (sem pontuaΓ§Γ£o). Precisamos | |
| # fazer join com espaΓ§os para obter texto bruto. Como o dataset usa | |
| # script Python (datasets 5.0+ nΓ£o suporta), usamos rows_api loader. | |
| # Splits: train/validation/test com config "punctuation-ptbr". | |
| "adalbertojunior/punctuation-ptbr": { | |
| "type": "punctuation_tokens", | |
| "text_fields": ["tokens"], # list[str] β join com espaΓ§os | |
| "label_fields": [], | |
| "split": "train", | |
| "config": "punctuation-ptbr", # necessΓ‘rio para rows_api | |
| "loader": "rows_api", | |
| "join_tokens": True, # flag especial para _normalize_sample | |
| }, | |
| # iara-project/news-articles-ptbr-dataset: | |
| # Dataset de notΓcias PT-BR da Folha de S.Paulo (via IARA project). | |
| # Schema: {title, text, date, category, category_natural_language, link} | |
| # Streaming normal via parquet β primeira amostra materializa rΓ‘pido. | |
| # "text" Γ© o corpo da notΓcia, "title" Γ© o tΓtulo. Concatenamos ambos | |
| # via format_template="title_text_concat" para enriquecer contexto. | |
| # O campo "category" Γ© usado como label (categoria da notΓcia). | |
| "iara-project/news-articles-ptbr-dataset": { | |
| "type": "news", | |
| # text_fields em ordem de prioridade: "text" (corpo) primeiro | |
| "text_fields": ["text", "title", "content", "body"], | |
| "label_fields": ["category", "category_natural_language", "title"], | |
| "split": "train", | |
| "config": None, | |
| # V6.5-7ds: title_text_concat usa title como label (prefixo) | |
| # e text como corpo. Aqui label_fields pega "category" primeiro | |
| # para dar contexto temΓ‘tico. | |
| "format_template": "title_text_concat", | |
| }, | |
| # manoela/noticias_ptbr: | |
| # Mirror do iara-project/news-articles-ptbr-dataset (mesmo parquet, | |
| # mesmo schema). Streaming normal via parquet. | |
| "manoela/noticias_ptbr": { | |
| "type": "news", | |
| "text_fields": ["text", "title", "content", "body"], | |
| "label_fields": ["category", "category_natural_language", "title"], | |
| "split": "train", | |
| "config": None, | |
| "format_template": "title_text_concat", | |
| }, | |
| } | |
| # Lista de arquivos XML.gz do Carolina que tentaremos baixar (ordem: menores | |
| # taxonomias primeiro para minimizar banda e RAM). Selecionamos 2 arquivos | |
| # por taxonomia para garantir diversidade sem explodir a memΓ³ria. | |
| CAROLINA_XML_FILES = [ | |
| # taxonomia "datasets_and_other_corpora" (DAT = pt-BR notΓcias) | |
| "corpus/datasets_and_other_corpora/pt-BR/DATa.xml.gz", | |
| "corpus/datasets_and_other_corpora/pt-BR/DATb.xml.gz", | |
| # taxonomia "wik" (wikis) | |
| "corpus/wik/WIKa.xml.gz", | |
| "corpus/wik/WIKb.xml.gz", | |
| # taxonomia "uni" (university_domains) | |
| "corpus/uni/UNIa.xml.gz", | |
| "corpus/uni/UNIb.xml.gz", | |
| # taxonomia "pub" (public_domain_works) | |
| "corpus/pub/PUBa.xml.gz", | |
| "corpus/pub/PUBb.xml.gz", | |
| ] | |
| def _stream_carolina_direct( | |
| max_samples: int, | |
| hf_token: Optional[str] = None, | |
| timeout_per_file: int = 60, | |
| ) -> Iterator[Dict[str, Any]]: | |
| """Workaround V13.9.2-carolina: carrega Carolina sem usar o script Python. | |
| Baixa arquivos XML.gz diretamente do repo e faz parse streaming com | |
| lxml.etree.iterparse (compatΓvel com datasets 5.0+). Cada <TEI> vira um | |
| documento de texto (concatenaΓ§Γ£o dos <p> dentro de <body>). | |
| Args: | |
| max_samples: nΓΊmero mΓ‘ximo de amostras a retornar | |
| hf_token: token HF opcional (dataset Γ© pΓΊblico, mas token evita rate limit) | |
| timeout_per_file: tempo mΓ‘ximo (segundos) por arquivo XML | |
| Yields: | |
| dict com chave "text" (compatΓvel com _normalize_sample) | |
| """ | |
| from huggingface_hub import hf_hub_download | |
| import gzip | |
| from lxml import etree | |
| TEI_NS = "{http://www.tei-c.org/ns/1.0}" | |
| BODY_P_TAG = f".//{TEI_NS}body/{TEI_NS}p" | |
| count = 0 | |
| for xml_path in CAROLINA_XML_FILES: | |
| if count >= max_samples: | |
| return | |
| try: | |
| local_path = hf_hub_download( | |
| repo_id="carolina-c4ai/corpus-carolina", | |
| filename=xml_path, | |
| repo_type="dataset", | |
| token=hf_token, | |
| ) | |
| except Exception as e: | |
| logger.warning(f"Carolina: nΓ£o foi possΓvel baixar {xml_path}: {e}") | |
| continue | |
| try: | |
| with gzip.open(local_path, "rb") as gz: | |
| # iterparse streaming: nΓ£o carrega Γ‘rvore inteira na memΓ³ria | |
| for _, tei in etree.iterparse( | |
| gz, huge_tree=True, encoding="utf-8", tag=f"{TEI_NS}TEI" | |
| ): | |
| if count >= max_samples: | |
| tei.clear() | |
| return | |
| # Extrai texto dos <p> dentro de <body> | |
| parts = [] | |
| for p in tei.findall(BODY_P_TAG): | |
| if p.text: | |
| parts.append(p.text) | |
| text = " ".join(parts).strip() | |
| if text and len(text) >= 10: | |
| yield {"text": text, "meta": ""} | |
| tei.clear() # libera memΓ³ria da Γ‘rvore TEI | |
| count += 1 | |
| except Exception as e: | |
| logger.warning(f"Carolina: erro processando {xml_path}: {e}") | |
| continue | |
| logger.info(f"Carolina: streamou {count} amostras no total") | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # V13.9.2-finetune-v2: Loaders alternativos para datasets parquet grandes | |
| # onde datasets.load_dataset(streaming=True) Γ© inviΓ‘vel (primeira amostra | |
| # demora minutos para materializar). | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| def _stream_via_rows_api( | |
| dataset_name: str, | |
| max_samples: int, | |
| hf_token: Optional[str] = None, | |
| batch_size: int = 100, | |
| max_offset: int = 100_000, | |
| config: Optional[str] = None, | |
| split: str = "train", | |
| ) -> Iterator[Dict[str, Any]]: | |
| """Carrega amostras via HF datasets-server /rows API (HTTP). | |
| Usado para datasets cujo parquet Γ© muito grande para streaming eficiente | |
| (ex: cnmoro/Instruct-PTBR-10M com 10 GB em arquivo ΓΊnico) OU datasets | |
| que usam script Python como loader (nΓ£o suportado em datasets 5.0+, | |
| ex: adalbertojunior/punctuation-ptbr). A rows API retorna batches de | |
| atΓ© 100 rows via HTTP, sem precisar baixar o parquet. | |
| Args: | |
| dataset_name: repo_id do dataset (ex: "cnmoro/Instruct-PTBR-10M") | |
| max_samples: nΒΊ mΓ‘ximo de amostras a retornar | |
| hf_token: token HF (opcional β dataset pΓΊblico) | |
| batch_size: nΒΊ de rows por requisiΓ§Γ£o (mΓ‘x 100) | |
| max_offset: offset mΓ‘ximo a tentar (sai do loop se excedido) | |
| config: nome da config do dataset (default: "default"). NECESSΓRIO | |
| para datasets cuja config nΓ£o Γ© "default" (ex: | |
| adalbertojunior/punctuation-ptbr usa "punctuation-ptbr"). | |
| split: nome do split (default: "train"). | |
| Yields: | |
| dict com campos do dataset (compatΓvel com _normalize_sample) | |
| """ | |
| import json as _json | |
| import urllib.request | |
| import urllib.parse | |
| base_url = "https://datasets-server.huggingface.co/rows" | |
| config_name = config or "default" | |
| count = 0 | |
| offset = 0 | |
| while count < max_samples and offset < max_offset: | |
| params = urllib.parse.urlencode({ | |
| "dataset": dataset_name, | |
| "config": config_name, | |
| "split": split, | |
| "offset": offset, | |
| "length": min(batch_size, max_samples - count), | |
| }) | |
| url = f"{base_url}?{params}" | |
| try: | |
| req = urllib.request.Request(url) | |
| if hf_token: | |
| req.add_header("Authorization", f"Bearer {hf_token}") | |
| with urllib.request.urlopen(req, timeout=30) as r: | |
| data = _json.loads(r.read()) | |
| except Exception as e: | |
| logger.warning(f"rows_api {dataset_name} config={config_name} offset={offset}: {e}") | |
| break | |
| rows = data.get("rows", []) | |
| if not rows: | |
| break | |
| for row in rows: | |
| if count >= max_samples: | |
| return | |
| row_data = row.get("row", {}) | |
| # Remove campos internos como __index_level_0__ | |
| clean = {k: v for k, v in row_data.items() | |
| if not k.startswith("__")} | |
| yield clean | |
| count += 1 | |
| offset += len(rows) | |
| if len(rows) < batch_size: | |
| break # chegou ao fim do dataset | |
| logger.info(f"rows_api {dataset_name} config={config_name}: streamou {count} amostras") | |
| def _stream_via_parquet_first_shard( | |
| dataset_name: str, | |
| max_samples: int, | |
| hf_token: Optional[str] = None, | |
| ) -> Iterator[Dict[str, Any]]: | |
| """Baixa primeiro shard parquet do dataset e itera localmente com pyarrow. | |
| Usado para datasets com mΓΊltiplos shards parquet grandes (ex: | |
| strak2005/corpus-ptbr-v1 com 18 shards de ~1GB). Streaming via | |
| datasets.load_dataset() nΓ£o materializa primeira amostra em tempo | |
| aceitΓ‘vel. Esta funΓ§Γ£o baixa o primeiro shard (~26s para 1GB) e itera | |
| localmente β rΓ‘pido apΓ³s download. | |
| Args: | |
| dataset_name: repo_id do dataset | |
| max_samples: nΒΊ mΓ‘ximo de amostras | |
| hf_token: token HF opcional | |
| Yields: | |
| dict com campos do dataset | |
| """ | |
| from huggingface_hub import HfApi, hf_hub_download | |
| import pyarrow.parquet as pq | |
| api = HfApi(token=hf_token) | |
| # Lista arquivos parquet no repo | |
| try: | |
| info = api.dataset_info(dataset_name) | |
| except Exception as e: | |
| logger.warning(f"parquet_shard {dataset_name}: dataset_info failed: {e}") | |
| return | |
| parquet_files = sorted([ | |
| s.rfilename for s in info.siblings | |
| if s.rfilename.endswith(".parquet") | |
| ]) | |
| if not parquet_files: | |
| logger.warning(f"parquet_shard {dataset_name}: nenhum .parquet encontrado") | |
| return | |
| # Baixa apenas o primeiro shard | |
| first_shard = parquet_files[0] | |
| logger.info(f"parquet_shard {dataset_name}: baixando {first_shard}...") | |
| try: | |
| local_path = hf_hub_download( | |
| repo_id=dataset_name, | |
| filename=first_shard, | |
| repo_type="dataset", | |
| token=hf_token, | |
| ) | |
| except Exception as e: | |
| logger.warning(f"parquet_shard {dataset_name}: download failed: {e}") | |
| return | |
| # Itera com pyarrow.iter_batches β mΓ©todo eficiente que lΓͺ batches pequenos | |
| # do parquet sem materializar o row group inteiro na memΓ³ria. | |
| # BUG FIX V13.9.2-finetune-v2: read_row_group() materializa TODAS as ~1M | |
| # rows de uma vez (lento + ~1GB RAM). iter_batches(batch_size=10) lΓͺ sΓ³ | |
| # 10 rows por vez (rΓ‘pido + ~10KB RAM). | |
| try: | |
| pf = pq.ParquetFile(local_path) | |
| count = 0 | |
| # batch_read_size: nΒΊ de rows por batch (pequeno = menos RAM) | |
| for batch in pf.iter_batches(batch_size=100): | |
| if count >= max_samples: | |
| break | |
| cols = batch.schema.names | |
| # to_pylist() Γ© eficiente para batches pequenos | |
| pydict = batch.to_pydict() | |
| n_rows = len(pydict[cols[0]]) if cols else 0 | |
| for i in range(n_rows): | |
| if count >= max_samples: | |
| break | |
| row = {col: pydict[col][i] for col in cols} | |
| yield row | |
| count += 1 | |
| except Exception as e: | |
| logger.warning(f"parquet_shard {dataset_name}: pyarrow iter failed: {e}") | |
| return | |
| logger.info(f"parquet_shard {dataset_name}: streamou {count} amostras do shard {first_shard}") | |
| class ProcessedSample: | |
| """Uma amostra processada: texto bruto + metadados.""" | |
| raw_text: str | |
| dataset_name: str | |
| sample_idx: int | |
| text_field: str = "" | |
| label_text: str = "" | |
| def _extract_field(sample: Dict[str, Any], candidate_fields: List[str]) -> Optional[str]: | |
| """Extrai texto da primeira field disponΓvel no sample. | |
| Suporta: | |
| - strings diretas | |
| - listas de strings (join com espaΓ§o β ΓΊtil para datasets tokenizados | |
| como adalbertojunior/punctuation-ptbr onde "tokens": ["hoje","a",...]) | |
| - listas de dicts (chat messages β concatena role: content) | |
| - dicts aninhados (pega text/content/body) | |
| """ | |
| for field_name in candidate_fields: | |
| if field_name in sample: | |
| val = sample[field_name] | |
| if val is None: | |
| continue | |
| if isinstance(val, str): | |
| if val.strip(): | |
| return val | |
| elif isinstance(val, list): | |
| # V6.5-7ds: se for lista de strings, join com espaΓ§os | |
| # (ex: adalbertojunior/punctuation-ptbr tokens field) | |
| string_parts = [] | |
| chat_parts = [] | |
| all_strings = True | |
| for item in val: | |
| if isinstance(item, str): | |
| if item.strip(): | |
| string_parts.append(item) | |
| elif isinstance(item, (int, float)): | |
| # tokens podem vir como ints (token IDs) β skip | |
| all_strings = False | |
| break | |
| elif isinstance(item, dict): | |
| all_strings = False | |
| role = item.get("role", "") | |
| content = item.get("content", "") | |
| if isinstance(content, str) and content.strip(): | |
| chat_parts.append(f"{role}: {content}") | |
| else: | |
| all_strings = False | |
| if all_strings and string_parts: | |
| joined = " ".join(string_parts).strip() | |
| if joined: | |
| return joined | |
| if chat_parts: | |
| return "\n".join(chat_parts) | |
| elif isinstance(val, dict): | |
| # Pode ser nested | |
| text = val.get("text") or val.get("content") or val.get("body") | |
| if isinstance(text, str) and text.strip(): | |
| return text | |
| return None | |
| def _normalize_sample(sample: Dict[str, Any], dataset_name: str, idx: int) -> Optional[ProcessedSample]: | |
| """Normaliza uma amostra bruta em ProcessedSample.""" | |
| fmt = DATASET_FORMATS.get(dataset_name, {}) | |
| text_fields = fmt.get("text_fields", ["text"]) | |
| label_fields = fmt.get("label_fields", []) | |
| text = _extract_field(sample, text_fields) | |
| if not text or len(text.strip()) < 10: # skip very short | |
| return None | |
| label = "" | |
| if label_fields: | |
| label = _extract_field(sample, label_fields) or "" | |
| # Se tem label, aplica template apropriado | |
| template = fmt.get("format_template") | |
| full_text = text | |
| # V13.9.2-finetune-v3: para o template instruction_response, exige ambos | |
| # os lados (instruction + response). Samples sem response sΓ£o descartados | |
| # para evitar treinar em texto parcial. | |
| if template == "instruction_response" and not label: | |
| return None | |
| if label: | |
| # V13.9.2-finetune-v3: format_template unificado "### Instruction:/### Response:" | |
| # quando o dataset declarar explicitamente. Caso contrΓ‘rio, mantΓ©m o | |
| # comportamento legado (concatenaΓ§Γ£o simples text\nlabel) para os 9 | |
| # datasets base β retrocompatibilidade total. | |
| if template == "instruction_response": | |
| instr_prefix = fmt.get("instruction_prefix", "") | |
| if instr_prefix: | |
| full_text = ( | |
| f"### Instruction:\n{instr_prefix}\n{text}\n\n" | |
| f"### Response:\n{label}" | |
| ) | |
| else: | |
| full_text = ( | |
| f"### Instruction:\n{text}\n\n" | |
| f"### Response:\n{label}" | |
| ) | |
| elif template == "title_text_concat": | |
| # V6.5-7ds: para datasets de notΓcias (iara-project, manoela), | |
| # o campo "text" (corpo da notΓcia) Γ© o conteΓΊdo principal e | |
| # "category" (categoria) Γ© o contexto temΓ‘tico. Formatamos como | |
| # "TEXTO: <corpo>\nCATEGORIA: <categoria>". | |
| full_text = f"TEXTO: {text}\nCATEGORIA: {label}" | |
| else: | |
| # Comportamento legado (9 datasets base): concatenaΓ§Γ£o simples | |
| full_text = f"{text}\n{label}" | |
| return ProcessedSample( | |
| raw_text=full_text, | |
| dataset_name=dataset_name, | |
| sample_idx=idx, | |
| text_field=text_fields[0] if text_fields else "", | |
| label_text=label, | |
| ) | |
| def _load_dataset_streaming( | |
| dataset_name: str, | |
| split: str = "train", | |
| config: Optional[str] = None, | |
| hf_token: Optional[str] = None, | |
| ): | |
| """Carrega dataset em modo streaming. | |
| BUG FIX V13.9.1: Removido trust_remote_code (deprecated). | |
| Se o dataset nΓ£o carregar com split especificado, tenta 'train' como fallback. | |
| BUG FIX V13.9.2: Adicionado fallback para datasets com script (carolina-c4ai). | |
| """ | |
| from datasets import load_dataset | |
| # Lista de splits para tentar, em ordem | |
| splits_to_try = [split] | |
| if split != "train": | |
| splits_to_try.append("train") | |
| # Adiciona outros splits comuns | |
| for s in ["test", "validation", "cot", "tir"]: | |
| if s not in splits_to_try: | |
| splits_to_try.append(s) | |
| fmt = DATASET_FORMATS.get(dataset_name, {}) | |
| fallback_name = fmt.get("fallback") | |
| datasets_to_try = [dataset_name] | |
| if fallback_name: | |
| datasets_to_try.append(fallback_name) | |
| for try_ds_name in datasets_to_try: | |
| for try_split in splits_to_try: | |
| try: | |
| if config: | |
| ds = load_dataset( | |
| try_ds_name, config, split=try_split, streaming=True, | |
| token=hf_token, | |
| ) | |
| else: | |
| ds = load_dataset( | |
| try_ds_name, split=try_split, streaming=True, | |
| token=hf_token, | |
| ) | |
| logger.info(f"Loaded {try_ds_name} split={try_split}") | |
| return ds | |
| except Exception as e: | |
| err_str = str(e).lower() | |
| # If "Bad split" error, try next split | |
| if "bad split" in err_str or "available splits" in err_str: | |
| logger.info(f" Split {try_split} not available for {try_ds_name}, trying next") | |
| continue | |
| # If "scripts no longer supported", try next dataset (fallback) | |
| if "scripts are no longer supported" in err_str and fallback_name: | |
| logger.info(f" {try_ds_name} uses script β trying fallback {fallback_name}") | |
| break | |
| # For other errors, log and try next split | |
| logger.warning(f"Failed to load {try_ds_name} split={try_split}: {str(e)[:120]}") | |
| continue | |
| logger.warning(f"All split attempts failed for {dataset_name} (will be skipped)") | |
| return None | |
| def stream_dataset( | |
| dataset_name: str, | |
| max_samples: int = 500, | |
| hf_token: Optional[str] = None, | |
| ) -> Iterator[ProcessedSample]: | |
| """Faz streaming de um dataset, retornando atΓ© max_samples amostras. | |
| Args: | |
| dataset_name: nome do dataset no HuggingFace | |
| max_samples: nΓΊmero mΓ‘ximo de amostras a retornar | |
| hf_token: token HF opcional | |
| Yields: | |
| ProcessedSample | |
| """ | |
| fmt = DATASET_FORMATS.get(dataset_name) | |
| if fmt is None: | |
| logger.error(f"Unknown dataset: {dataset_name}") | |
| return | |
| # ββ V13.9.2-carolina: bypass do script Python βββββββββββββββββββββββββ | |
| # O dataset carolina-c4ai/corpus-carolina usa um script Python como loader, | |
| # que nΓ£o Γ© suportado por datasets 5.0+. Streamamos os XML.gz diretamente. | |
| if dataset_name == "carolina-c4ai/corpus-carolina": | |
| logger.info(f"Carolina: usando carregador direto (bypass do script Python)") | |
| count = 0 | |
| try: | |
| for raw_sample in _stream_carolina_direct( | |
| max_samples=max_samples, hf_token=hf_token | |
| ): | |
| processed = _normalize_sample(raw_sample, dataset_name, count) | |
| if processed is not None: | |
| yield processed | |
| count += 1 | |
| except Exception as e: | |
| logger.warning(f"Carolina direct stream error at sample {count}: {e}") | |
| return | |
| logger.info(f"Streamed {count} samples from {dataset_name} (direct XML)") | |
| return | |
| # ββ V13.9.2-finetune-v2: loaders alternativos para parquets grandes βββ | |
| # Alguns datasets (cnmoro, strak2005/bratao) tΓͺm parquets tΓ£o grandes que | |
| # datasets.load_dataset(streaming=True) demora minutos para materializar | |
| # a primeira amostra. Usamos loaders alternativos especificados no campo | |
| # "loader" do formato. | |
| loader = fmt.get("loader") | |
| if loader == "rows_api": | |
| logger.info(f"{dataset_name}: usando rows_api loader (datasets-server HTTP)") | |
| count = 0 | |
| try: | |
| for raw_sample in _stream_via_rows_api( | |
| dataset_name, max_samples=max_samples, hf_token=hf_token, | |
| config=fmt.get("config"), | |
| split=fmt.get("split", "train"), | |
| ): | |
| processed = _normalize_sample(raw_sample, dataset_name, count) | |
| if processed is not None: | |
| yield processed | |
| count += 1 | |
| except Exception as e: | |
| logger.warning(f"rows_api stream error at sample {count}: {e}") | |
| return | |
| logger.info(f"Streamed {count} samples from {dataset_name} (rows_api)") | |
| return | |
| if loader == "parquet_first_shard": | |
| logger.info(f"{dataset_name}: usando parquet_first_shard loader") | |
| count = 0 | |
| try: | |
| for raw_sample in _stream_via_parquet_first_shard( | |
| dataset_name, max_samples=max_samples, hf_token=hf_token | |
| ): | |
| processed = _normalize_sample(raw_sample, dataset_name, count) | |
| if processed is not None: | |
| yield processed | |
| count += 1 | |
| except Exception as e: | |
| logger.warning(f"parquet_shard stream error at sample {count}: {e}") | |
| # Tenta fallback (ex: strak2005 -> bratao) | |
| fallback_name = fmt.get("fallback") | |
| if fallback_name and fallback_name != dataset_name: | |
| logger.info(f"Tentando fallback: {fallback_name}") | |
| fallback_fmt = DATASET_FORMATS.get(fallback_name, {}) | |
| # RecursΓ£o respeitando o loader do fallback | |
| yield from stream_dataset( | |
| fallback_name, max_samples=max_samples, hf_token=hf_token | |
| ) | |
| return | |
| logger.info(f"Streamed {count} samples from {dataset_name} (parquet_first_shard)") | |
| return | |
| ds = _load_dataset_streaming( | |
| dataset_name, | |
| split=fmt.get("split", "train"), | |
| config=fmt.get("config"), | |
| hf_token=hf_token, | |
| ) | |
| if ds is None: | |
| return | |
| count = 0 | |
| try: | |
| for idx, sample in enumerate(ds): | |
| if count >= max_samples: | |
| break | |
| processed = _normalize_sample(sample, dataset_name, idx) | |
| if processed is not None: | |
| yield processed | |
| count += 1 | |
| except Exception as e: | |
| logger.warning(f"Error streaming {dataset_name} at sample {idx}: {e}") | |
| return | |
| logger.info(f"Streamed {count} samples from {dataset_name}") | |
| def stream_all_datasets( | |
| datasets: Optional[List[str]] = None, | |
| max_samples_per_dataset: int = 500, | |
| hf_token: Optional[str] = None, | |
| ) -> Iterator[ProcessedSample]: | |
| """Faz streaming sequencial de mΓΊltiplos datasets. | |
| Args: | |
| datasets: lista de nomes (default: DEFAULT_DATASETS) | |
| max_samples_per_dataset: limite por dataset | |
| hf_token: token HF opcional | |
| Yields: | |
| ProcessedSample | |
| """ | |
| if datasets is None: | |
| datasets = DEFAULT_DATASETS | |
| for ds_name in datasets: | |
| logger.info(f"Starting dataset: {ds_name}") | |
| yield from stream_dataset( | |
| ds_name, | |
| max_samples=max_samples_per_dataset, | |
| hf_token=hf_token, | |
| ) | |
| def collate_samples_to_tensors( | |
| samples: List[ProcessedSample], | |
| tokenizer, | |
| max_seq_len: int = 64, | |
| pad_token_id: int = 0, | |
| ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]: | |
| """Converte lista de ProcessedSample em tensors para treino. | |
| BUG FIX V13.9.1: Tokenizer.encode() retorna 'Encoding' object, nΓ£o list. | |
| Use .ids attribute to get token IDs. | |
| Returns: | |
| input_ids: (B, T) | |
| attention_mask: (B, T) | |
| labels: (B, T) β same as input_ids (LM training) | |
| """ | |
| batch_input_ids = [] | |
| batch_attn_mask = [] | |
| for sample in samples: | |
| # Tokenize | |
| encoding = tokenizer.encode(sample.raw_text, add_special_tokens=True) | |
| # BUG FIX: Handle both `tokenizers.Encoding` and `transformers` tokenizer outputs | |
| if hasattr(encoding, 'ids'): | |
| tokens = encoding.ids # tokenizers library | |
| elif isinstance(encoding, list): | |
| tokens = encoding # already a list of ints | |
| elif hasattr(encoding, 'input_ids'): | |
| tokens = encoding.input_ids # transformers BatchEncoding | |
| else: | |
| tokens = list(encoding) # fallback | |
| # Truncate to max_seq_len | |
| if len(tokens) > max_seq_len: | |
| tokens = tokens[:max_seq_len] | |
| # Pad | |
| attn_mask = [1] * len(tokens) + [0] * (max_seq_len - len(tokens)) | |
| tokens = list(tokens) + [pad_token_id] * (max_seq_len - len(tokens)) | |
| batch_input_ids.append(tokens) | |
| batch_attn_mask.append(attn_mask) | |
| input_ids = torch.tensor(batch_input_ids, dtype=torch.long) | |
| attention_mask = torch.tensor(batch_attn_mask, dtype=torch.long) | |
| labels = input_ids.clone() | |
| return input_ids, attention_mask, labels | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # Self-test (NΓO enviar para HuggingFace) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if __name__ == "__main__": | |
| print("=== Streaming Datasets V13.9.1 Self-Test ===\n") | |
| # Test 1: Test field extraction | |
| print("Test 1: Field extraction") | |
| sample_chat = { | |
| "messages": [ | |
| {"role": "user", "content": "OlΓ‘"}, | |
| {"role": "assistant", "content": "Como posso ajudar?"}, | |
| ] | |
| } | |
| text = _extract_field(sample_chat, ["messages", "text"]) | |
| print(f" Chat text: {text!r}") | |
| assert text and "OlΓ‘" in text, "Field extraction failed for chat" | |
| sample_text = {"text": "Hello world"} | |
| text = _extract_field(sample_text, ["text", "content"]) | |
| assert text == "Hello world" | |
| print(f" Text field: {text!r}") | |
| print(f" OK\n") | |
| # Test 2: Test list of datasets | |
| print("Test 2: Dataset list") | |
| print(f" Configured datasets ({len(DEFAULT_DATASETS)}):") | |
| for i, ds in enumerate(DEFAULT_DATASETS, 1): | |
| print(f" {i}. {ds}") | |
| assert len(DEFAULT_DATASETS) == 13 | |
| print(f" OK\n") | |
| print("=== ALL STREAMING TESTS PASSED ===") | |