Coding-With-Bashir commited on
Commit
0a5b9ed
·
verified ·
1 Parent(s): 17437d3

Upload .\src\data_collection\huggingface_collector.py with huggingface_hub

Browse files
.//src//data_collection//huggingface_collector.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Huggingface dataset collector for Kinyarwanda data."""
2
+
3
+ import json
4
+ import logging
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from datasets import load_dataset
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+ MAX_ROWS_DEFAULT = 2_000_000_000
13
+
14
+ DATASET_MAX_ROWS = {
15
+ "HuggingFaceFW/fineweb-2": 500_000_000,
16
+ "HPLT/HPLT2.0_cleaned": 100_000_000,
17
+ "yhavinga/ccmatrix": 100_000_000,
18
+ "sentence-transformers/parallel-sentences-ccmatrix": 50_000_000,
19
+ "datalama/pretrain-nllb-filtered": 50_000_000,
20
+ "hotchpotch/nllb-english-bitext-hq": 50_000_000,
21
+ "NaolBM/african-corpus": 10_000_000,
22
+ "bonadossou/afrolm_active_learning_dataset": 5_000_000,
23
+ "wikimedia/wikidata-title-desc": 10_000_000,
24
+ "wikimedia/wikipedia": 5_000_000,
25
+ "csebuetnlp/xlsum": 2_000_000,
26
+ "csebuetnlp/mt5_xlsum_all_languages_44": 1_000_000,
27
+ }
28
+
29
+
30
+ class HuggingfaceCollector:
31
+ """Collects Kinyarwanda datasets from Huggingface."""
32
+
33
+ def __init__(self, output_dir: str, config: dict[str, Any]):
34
+ self.output_dir = Path(output_dir)
35
+ self.output_dir.mkdir(parents=True, exist_ok=True)
36
+ self.config = config
37
+ self.datasets_info: list[dict[str, Any]] = []
38
+
39
+ def collect_dataset(self, dataset_config: dict[str, str]) -> dict[str, Any]:
40
+ """Download and save a single dataset using streaming."""
41
+ name = dataset_config["name"]
42
+ split = dataset_config.get("split", "train")
43
+
44
+ safe_name = name.replace("/", "_")
45
+ output_path = self.output_dir / f"{safe_name}.jsonl"
46
+ done_marker = self.output_dir / f"{safe_name}.jsonl.done"
47
+
48
+ if done_marker.exists():
49
+ try:
50
+ row_count = int(done_marker.read_text().strip())
51
+ logger.info(f"Already collected: {name} ({row_count} rows), skipping")
52
+ return {
53
+ "name": name,
54
+ "split": split,
55
+ "rows": row_count,
56
+ "output_path": str(output_path),
57
+ "description": dataset_config.get("description", ""),
58
+ "status": "success",
59
+ }
60
+ except (ValueError, OSError):
61
+ pass
62
+
63
+ logger.info(f"Collecting dataset: {name} (split: {split})")
64
+
65
+ max_rows = DATASET_MAX_ROWS.get(name, MAX_ROWS_DEFAULT)
66
+
67
+ try:
68
+ dataset = load_dataset(name, split=split, streaming=True)
69
+
70
+ count = 0
71
+ with open(output_path, "w", encoding="utf-8") as f:
72
+ for item in dataset:
73
+ f.write(json.dumps(item, ensure_ascii=False) + "\n")
74
+ count += 1
75
+ if count >= max_rows:
76
+ logger.info(f" Reached max_rows limit ({max_rows}) for {name}")
77
+ break
78
+ if count % 100_000 == 0:
79
+ logger.info(f" {name}: {count:,} rows collected...")
80
+
81
+ done_marker.write_text(str(count))
82
+
83
+ info = {
84
+ "name": name,
85
+ "split": split,
86
+ "rows": count,
87
+ "output_path": str(output_path),
88
+ "description": dataset_config.get("description", ""),
89
+ "status": "success",
90
+ }
91
+
92
+ logger.info(f" Saved {count:,} rows to {output_path}")
93
+ self.datasets_info.append(info)
94
+ return info
95
+
96
+ except Exception as e:
97
+ logger.error(f" Failed to collect {name}: {e}")
98
+ info = {
99
+ "name": name,
100
+ "split": split,
101
+ "status": "failed",
102
+ "error": str(e),
103
+ "description": dataset_config.get("description", ""),
104
+ }
105
+ self.datasets_info.append(info)
106
+ return info
107
+
108
+ def collect_all(self) -> list[dict[str, Any]]:
109
+ """Collect all configured datasets."""
110
+ datasets_config = self.config.get("huggingface", {}).get("datasets", [])
111
+
112
+ logger.info(f"Collecting {len(datasets_config)} datasets from Huggingface...")
113
+
114
+ for dataset_config in datasets_config:
115
+ self.collect_dataset(dataset_config)
116
+
117
+ summary_path = self.output_dir / "huggingface_summary.json"
118
+ with open(summary_path, "w", encoding="utf-8") as f:
119
+ json.dump(self.datasets_info, f, indent=2, ensure_ascii=False)
120
+
121
+ successful = sum(1 for d in self.datasets_info if d["status"] == "success")
122
+ failed = sum(1 for d in self.datasets_info if d["status"] == "failed")
123
+ total_rows = sum(d.get("rows", 0) for d in self.datasets_info if d["status"] == "success")
124
+
125
+ logger.info(f"Collection complete: {successful} succeeded, {failed} failed, {total_rows:,} total rows")
126
+
127
+ return self.datasets_info
128
+
129
+ def get_text_samples(self, max_samples: int = 1000) -> list[str]:
130
+ """Extract text samples from all collected datasets."""
131
+ texts = []
132
+
133
+ for info in self.datasets_info:
134
+ if info["status"] != "success":
135
+ continue
136
+
137
+ output_path = Path(info["output_path"])
138
+ if not output_path.exists():
139
+ continue
140
+
141
+ with open(output_path, "r", encoding="utf-8") as f:
142
+ for i, line in enumerate(f):
143
+ if i >= max_samples:
144
+ break
145
+
146
+ item = json.loads(line)
147
+
148
+ text = self._extract_text(item)
149
+ if text:
150
+ texts.append(text)
151
+
152
+ return texts
153
+
154
+ def _extract_text(self, item: dict[str, Any]) -> str | None:
155
+ """Extract text content from a dataset item."""
156
+ text_keys = ["text", "content", "sentence", "document", "paragraph", "translation"]
157
+
158
+ for key in text_keys:
159
+ if key in item:
160
+ value = item[key]
161
+ if isinstance(value, str):
162
+ return value
163
+ elif isinstance(value, dict):
164
+ for subkey in ["rw", "rwanda", "kinyarwanda", "kin"]:
165
+ if subkey in value:
166
+ return value[subkey]
167
+
168
+ for key in item:
169
+ if isinstance(item[key], str) and len(item[key]) > 50:
170
+ return item[key]
171
+
172
+ return None