eeeeeeeeeeeeee3 commited on
Commit
58bebae
Β·
verified Β·
1 Parent(s): 19906aa

Upload scripts/push_to_huggingface.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. scripts/push_to_huggingface.py +423 -0
scripts/push_to_huggingface.py ADDED
@@ -0,0 +1,423 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Push soccer ball detection project to Hugging Face Hub.
4
+ Includes code, configs, and trained model checkpoints.
5
+ """
6
+
7
+ import argparse
8
+ import os
9
+ import shutil
10
+ import sys
11
+ from pathlib import Path
12
+ from typing import List, Optional
13
+
14
+ try:
15
+ from huggingface_hub import HfApi, create_repo, login
16
+ from huggingface_hub.utils import HfHubHTTPError
17
+ HF_AVAILABLE = True
18
+ except ImportError:
19
+ HF_AVAILABLE = False
20
+ print("Error: huggingface_hub not installed. Install with: pip install huggingface_hub")
21
+ sys.exit(1)
22
+
23
+
24
+ def get_files_to_upload(project_root: Path, exclude_patterns: List[str] = None) -> List[Path]:
25
+ """Get list of files to upload, excluding patterns."""
26
+ if exclude_patterns is None:
27
+ exclude_patterns = [
28
+ '__pycache__',
29
+ '.git',
30
+ '.gitignore',
31
+ 'venv',
32
+ 'env',
33
+ '.venv',
34
+ '.env',
35
+ '.env.local',
36
+ 'mlruns',
37
+ 'logs',
38
+ '*.log',
39
+ '.DS_Store',
40
+ 'Thumbs.db',
41
+ 'data/raw',
42
+ 'data/output',
43
+ 'datasets',
44
+ '*.tar.gz',
45
+ '*.tar',
46
+ '*.zip',
47
+ ]
48
+
49
+ files_to_upload = []
50
+
51
+ # Files/directories to include
52
+ include_patterns = [
53
+ '*.py',
54
+ '*.yaml',
55
+ '*.yml',
56
+ '*.md',
57
+ '*.txt',
58
+ '*.json',
59
+ '*.html',
60
+ '*.sh',
61
+ '*.dockerfile',
62
+ 'Dockerfile',
63
+ 'docker-compose*.yml',
64
+ 'configs/',
65
+ 'src/',
66
+ 'scripts/',
67
+ 'models/checkpoints/*.pth', # Include model checkpoints
68
+ ]
69
+
70
+ def should_include(file_path: Path) -> bool:
71
+ """Check if file should be included."""
72
+ rel_path = file_path.relative_to(project_root)
73
+ rel_str = str(rel_path)
74
+
75
+ # Check exclude patterns
76
+ for pattern in exclude_patterns:
77
+ if pattern in rel_str or file_path.name.startswith('.'):
78
+ # Special handling for .gitkeep files
79
+ if file_path.name == '.gitkeep':
80
+ return True
81
+ return False
82
+
83
+ # Check include patterns
84
+ for pattern in include_patterns:
85
+ if pattern.endswith('/'):
86
+ # Directory pattern
87
+ if rel_str.startswith(pattern[:-1]):
88
+ return True
89
+ elif '*' in pattern:
90
+ # Wildcard pattern
91
+ import fnmatch
92
+ if fnmatch.fnmatch(file_path.name, pattern):
93
+ return True
94
+ else:
95
+ # Exact match
96
+ if file_path.name == pattern or rel_str == pattern:
97
+ return True
98
+
99
+ return False
100
+
101
+ # Walk through project directory
102
+ for root, dirs, files in os.walk(project_root):
103
+ root_path = Path(root)
104
+
105
+ # Filter directories to skip
106
+ dirs[:] = [d for d in dirs if not any(pattern in d for pattern in exclude_patterns)]
107
+
108
+ # Check files
109
+ for file in files:
110
+ file_path = root_path / file
111
+ if should_include(file_path):
112
+ files_to_upload.append(file_path)
113
+
114
+ return sorted(files_to_upload)
115
+
116
+
117
+ def create_hf_readme(project_root: Path, repo_id: str) -> str:
118
+ """Create README content for Hugging Face."""
119
+ readme_content = f"""---
120
+ license: mit
121
+ tags:
122
+ - computer-vision
123
+ - object-detection
124
+ - soccer
125
+ - ball-detection
126
+ - detr
127
+ - rf-detr
128
+ - pytorch
129
+ datasets:
130
+ - custom
131
+ metrics:
132
+ - mAP
133
+ - precision
134
+ - recall
135
+ ---
136
+
137
+ # Soccer Ball Detection with RF-DETR
138
+
139
+ Automated soccer ball detection pipeline using RF-DETR (Roboflow DETR) optimized for tiny object detection (<15 pixels).
140
+
141
+ ## Model Details
142
+
143
+ ### Architecture
144
+ - **Model**: RF-DETR Base
145
+ - **Backbone**: ResNet-50
146
+ - **Classes**: Ball (single class detection)
147
+ - **Input Resolution**: 1120x1120 (optimized for memory)
148
+ - **Precision**: Mixed Precision (FP16/FP32) training, FP16 inference
149
+
150
+ ### Performance
151
+ Based on training evaluation report (Epoch 39):
152
+ - **mAP@0.5:0.95**: 0.682 (68.2%)
153
+ - **mAP@0.5**: 0.990 (99.0%)
154
+ - **Small Objects mAP**: 0.598 (59.8%)
155
+ - **Training Loss**: 3.073
156
+ - **Validation Loss**: 3.658
157
+
158
+ ## Quick Start
159
+
160
+ ### Installation
161
+
162
+ ```bash
163
+ pip install -r requirements.txt
164
+ ```
165
+
166
+ ### Usage
167
+
168
+ ```python
169
+ from src.perception.local_detector import LocalDetector
170
+ from pathlib import Path
171
+
172
+ # Initialize detector
173
+ detector = LocalDetector(
174
+ model_path="models/checkpoints/latest_checkpoint.pth",
175
+ config_path="configs/default.yaml"
176
+ )
177
+
178
+ # Detect ball in image
179
+ results = detector.detect(image_path)
180
+ ```
181
+
182
+ ### Process Video
183
+
184
+ ```bash
185
+ python main.py --video path/to/video.mp4 --config configs/default.yaml --output data/output
186
+ ```
187
+
188
+ ## Training
189
+
190
+ ### Train from Scratch
191
+
192
+ ```bash
193
+ python scripts/train_ball.py \\
194
+ --config configs/training.yaml \\
195
+ --dataset-dir datasets/combined \\
196
+ --output-dir models \\
197
+ --epochs 50
198
+ ```
199
+
200
+ ### Resume Training
201
+
202
+ ```bash
203
+ python scripts/train_ball.py \\
204
+ --config configs/resume_20_epochs.yaml \\
205
+ --dataset-dir datasets/combined \\
206
+ --output-dir models \\
207
+ --resume models/checkpoints/latest_checkpoint.pth \\
208
+ --epochs 50
209
+ ```
210
+
211
+ ## Project Structure
212
+
213
+ ```
214
+ soccer_cv_ball/
215
+ β”œβ”€β”€ main.py # Main orchestrator
216
+ β”œβ”€β”€ configs/ # Configuration files
217
+ β”œβ”€β”€ src/
218
+ β”‚ β”œβ”€β”€ perception/ # Detection, tracking
219
+ β”‚ β”œβ”€β”€ analysis/ # Event detection
220
+ β”‚ β”œβ”€β”€ visualization/ # Dashboard
221
+ β”‚ └── training/ # Training utilities
222
+ β”œβ”€β”€ scripts/ # Training and utility scripts
223
+ β”œβ”€β”€ models/ # Model checkpoints
224
+ └── data/ # Dataset (not included)
225
+ ```
226
+
227
+ ## Configuration
228
+
229
+ Key configuration files:
230
+ - `configs/training.yaml` - Main training configuration
231
+ - `configs/default.yaml` - Inference configuration
232
+ - `configs/resume_*.yaml` - Resume training configurations
233
+
234
+ ## Datasets
235
+
236
+ This model was trained on:
237
+ - SoccerSynth-Detection (synthetic data)
238
+ - Open Soccer Ball Dataset
239
+ - Custom validation sets
240
+
241
+ ## Precision Strategy
242
+
243
+ - **Training**: Mixed Precision (FP16/FP32) - RF-DETR default
244
+ - **Inference**: FP16 (half precision) for ~3x speedup
245
+ - **Future**: INT8 via QAT (Quantization-Aware Training) for edge devices
246
+
247
+ ## Citation
248
+
249
+ If you use this model, please cite:
250
+
251
+ ```bibtex
252
+ @software{{soccer_ball_detection,
253
+ title={{Soccer Ball Detection with RF-DETR}},
254
+ author={{Your Name}},
255
+ year={{2026}},
256
+ url={{https://huggingface.co/{repo_id}}}
257
+ }}
258
+ ```
259
+
260
+ ## License
261
+
262
+ MIT License - See LICENSE file for details.
263
+
264
+ ## Acknowledgments
265
+
266
+ - RF-DETR by Roboflow
267
+ - SoccerSynth-Detection dataset
268
+ - Open Soccer Ball Dataset
269
+ """
270
+ return readme_content
271
+
272
+
273
+ def push_to_huggingface(
274
+ repo_id: str,
275
+ project_root: Path,
276
+ token: Optional[str] = None,
277
+ private: bool = False,
278
+ include_models: bool = True
279
+ ):
280
+ """Push project to Hugging Face Hub using HTTP API."""
281
+
282
+ project_root = Path(project_root).resolve()
283
+
284
+ print(f"πŸ“¦ Preparing to push to Hugging Face: {repo_id}")
285
+ print(f" Project root: {project_root}")
286
+
287
+ # Initialize API
288
+ api = HfApi(token=token)
289
+
290
+ # Verify login
291
+ try:
292
+ user_info = api.whoami()
293
+ print(f"πŸ” Logged in as: {user_info.get('name', 'unknown')}")
294
+ except Exception as e:
295
+ print(f"❌ Authentication failed: {e}")
296
+ print(" Please provide a valid token with --token argument")
297
+ sys.exit(1)
298
+
299
+ # Create repository
300
+ print(f"πŸ“ Creating repository: {repo_id}")
301
+ try:
302
+ api.create_repo(repo_id, private=private, exist_ok=True, repo_type="model")
303
+ print("βœ… Repository created/exists")
304
+ except HfHubHTTPError as e:
305
+ if "already exists" in str(e).lower():
306
+ print("βœ… Repository already exists")
307
+ else:
308
+ print(f"⚠️ Repository creation warning: {e}")
309
+
310
+ # Get files to upload
311
+ print("πŸ” Finding files to upload...")
312
+ files_to_upload = get_files_to_upload(project_root)
313
+ print(f" Found {len(files_to_upload)} files")
314
+
315
+ # Filter out model files if not including models
316
+ if not include_models:
317
+ files_to_upload = [f for f in files_to_upload if not str(f).endswith('.pth')]
318
+ print(f" Excluding model files: {len(files_to_upload)} files remaining")
319
+
320
+ # Create README
321
+ print("πŸ“ Creating README.md...")
322
+ readme_path = project_root / "README.md"
323
+ readme_content = create_hf_readme(project_root, repo_id)
324
+ with open(readme_path, 'w') as f:
325
+ f.write(readme_content)
326
+ print("βœ… README.md created")
327
+
328
+ # Upload files
329
+ print("πŸ“€ Uploading files to Hugging Face Hub...")
330
+ uploaded = 0
331
+ failed = 0
332
+
333
+ for file_path in files_to_upload:
334
+ try:
335
+ rel_path = file_path.relative_to(project_root)
336
+ # Convert to forward slashes for HF
337
+ path_in_repo = str(rel_path).replace('\\', '/')
338
+
339
+ # Skip if it's the original README (we'll upload our generated one)
340
+ if path_in_repo == "README.md" and file_path != readme_path:
341
+ continue
342
+
343
+ # Upload file
344
+ api.upload_file(
345
+ path_or_fileobj=str(file_path),
346
+ path_in_repo=path_in_repo,
347
+ repo_id=repo_id,
348
+ repo_type="model",
349
+ token=token
350
+ )
351
+ uploaded += 1
352
+ if uploaded % 10 == 0:
353
+ print(f" Uploaded {uploaded}/{len(files_to_upload)} files...")
354
+ except Exception as e:
355
+ failed += 1
356
+ print(f" ⚠️ Failed to upload {file_path.name}: {e}")
357
+
358
+ # Upload the generated README
359
+ try:
360
+ api.upload_file(
361
+ path_or_fileobj=str(readme_path),
362
+ path_in_repo="README.md",
363
+ repo_id=repo_id,
364
+ repo_type="model",
365
+ token=token
366
+ )
367
+ print("βœ… README.md uploaded")
368
+ except Exception as e:
369
+ print(f"⚠️ Failed to upload README.md: {e}")
370
+
371
+ print(f"\nβœ… Upload complete!")
372
+ print(f" Uploaded: {uploaded} files")
373
+ if failed > 0:
374
+ print(f" Failed: {failed} files")
375
+ print(f" View at: https://huggingface.co/{repo_id}")
376
+
377
+
378
+ def main():
379
+ parser = argparse.ArgumentParser(
380
+ description="Push soccer ball detection project to Hugging Face Hub"
381
+ )
382
+ parser.add_argument(
383
+ "--repo-id",
384
+ type=str,
385
+ required=True,
386
+ help="Hugging Face repository ID (e.g., 'username/soccer-ball-detection')"
387
+ )
388
+ parser.add_argument(
389
+ "--project-root",
390
+ type=str,
391
+ default=".",
392
+ help="Project root directory (default: current directory)"
393
+ )
394
+ parser.add_argument(
395
+ "--token",
396
+ type=str,
397
+ default=None,
398
+ help="Hugging Face API token (or use huggingface-cli login)"
399
+ )
400
+ parser.add_argument(
401
+ "--private",
402
+ action="store_true",
403
+ help="Create private repository"
404
+ )
405
+ parser.add_argument(
406
+ "--no-models",
407
+ action="store_true",
408
+ help="Exclude model checkpoint files (.pth)"
409
+ )
410
+
411
+ args = parser.parse_args()
412
+
413
+ push_to_huggingface(
414
+ repo_id=args.repo_id,
415
+ project_root=Path(args.project_root),
416
+ token=args.token,
417
+ private=args.private,
418
+ include_models=not args.no_models
419
+ )
420
+
421
+
422
+ if __name__ == "__main__":
423
+ main()