Shanmuk4622 commited on
Commit
54418ce
Β·
verified Β·
1 Parent(s): 6925b37

Upload eden_hf_upload.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. eden_hf_upload.py +458 -0
eden_hf_upload.py ADDED
@@ -0,0 +1,458 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Project EDEN - Hugging Face Upload Master Script
3
+ Applies all 6 refinements:
4
+ 1. Hardware transparency (1080 Ti / Xeon W-2125)
5
+ 2. E2AM Phase mapping per model
6
+ 3. Phase 1 Zero-Overhead Initialization highlight
7
+ 4. Standardized Green Delta table in every README
8
+ 5. YAML tags with co2_eq_emissions + dataset_size
9
+ 6. Citation section in Main Repo
10
+ """
11
+
12
+ import os
13
+ import json
14
+ import glob
15
+ import math
16
+ from huggingface_hub import HfApi, create_repo, upload_file
17
+
18
+ # ─── CONFIG ──────────────────────────────────────────────────────────────────
19
+ HF_TOKEN = os.environ.get("HF_TOKEN", "")
20
+ HF_USER = "Shanmuk4622" # HF username (no org found, uploading under user)
21
+ HF_ORG = HF_USER # use user namespace
22
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
23
+ DRY_RUN = False # Live upload
24
+
25
+ api = HfApi(token=HF_TOKEN)
26
+
27
+ # ─── HARDWARE PROFILE ────────────────────────────────────────────────────────
28
+ HARDWARE = {
29
+ "gpu": "NVIDIA GeForce GTX 1080 Ti (11 GB VRAM, 250 W TDP)",
30
+ "cpu": "Intel Xeon W-2125 (4 cores / 8 threads @ 4.00 GHz)",
31
+ "ram": "63.66 GB System RAM",
32
+ "os": "Windows 10",
33
+ }
34
+
35
+ # ─── E2AM PHASE MAP ──────────────────────────────────────────────────────────
36
+ # Maps folder -> technique label for README
37
+ PHASE_MAP = {
38
+ "test1": "Phase 2 – Progressive Unfreezing + AMP (E2AM SOTA)",
39
+ "test2": "Baseline – Standard Full Training (Reference Study)",
40
+ "test3": "Phase 2 – EDEN Classic Energy-Aware Sparse Training",
41
+ }
42
+
43
+ PHASE_DETAIL = {
44
+ "test1": (
45
+ "**Phase 1 – Zero-Overhead Initialization:** Dataset pre-loaded into pinned "
46
+ "System RAM to eliminate disk I/O power spikes.\n\n"
47
+ "**Phase 2 – Progressive Unfreezing:** Backbone frozen for the first "
48
+ "`E_unfreeze` epochs (only the classification head trains). At `E_unfreeze`, "
49
+ "all layers are unfrozen and the learning rate is decayed. "
50
+ "Gradient accumulation over N micro-batches simulates large batch sizes "
51
+ "without proportional VRAM cost, slashing power-draw spikes.\n\n"
52
+ "**AMP (Automated Mixed Precision):** `torch.cuda.amp.autocast()` halves "
53
+ "GPU memory bandwidth, reducing energy per backward pass.\n\n"
54
+ "**Sparse Regularisation:** L1 penalty `λ·Σ|W|` applied to trainable "
55
+ "weights, driving dead neurons to zero and enabling future pruning."
56
+ ),
57
+ "test2": (
58
+ "Standard full fine-tuning used as the **Brute-Force Baseline** for "
59
+ "energy comparison. All layers trained from epoch 1 with a fixed learning "
60
+ "rate and no gradient accumulation. Included for transparent EAG benchmarking."
61
+ ),
62
+ "test3": (
63
+ "**Phase 1 – Zero-Overhead Initialization:** Dataset cached in System RAM.\n\n"
64
+ "**Phase 2 – EDEN Classic:** Energy-aware training loop on classic CNN "
65
+ "architectures. Applies the same EAG early-exit criterion "
66
+ "(`EAG < Ξ³_EAG` for 3 consecutive epochs β†’ terminate), L1 sparsity "
67
+ "penalty, and AMP to architectures like ResNet, VGG, AlexNet, DenseNet, "
68
+ "InceptionV3, and UNet."
69
+ ),
70
+ }
71
+
72
+ # ─── DATASET META ────────────────────────────────────────────────────────────
73
+ DATASET_META = {
74
+ "CIFAR-10": {"size": "60,000 images – 10 classes (32Γ—32 px)", "hf_name": "cifar10"},
75
+ "CIFAR-100": {"size": "60,000 images – 100 classes (32Γ—32 px)", "hf_name": "cifar100"},
76
+ "Custom-ImageNet300": {"size": "~450,000 images – 300 classes (224 px)", "hf_name": "imagenet"},
77
+ "unknown": {"size": "N/A", "hf_name": "unknown"},
78
+ }
79
+
80
+ # CO2: 0.475 kg CO2e per kWh (global average grid factor)
81
+ KG_CO2_PER_KWH = 0.000000475 # per Joule
82
+
83
+ # ─── HELPERS ─────────────────────────────────────────────────────────────────
84
+ def parse_name(filename):
85
+ fn = filename.lower().replace("\\", "/")
86
+ dataset = "unknown"
87
+ arch = "unknown"
88
+ if "cifar100" in fn: dataset = "CIFAR-100"
89
+ elif "cifar10" in fn: dataset = "CIFAR-10"
90
+ elif "imagenet" in fn: dataset = "Custom-ImageNet300"
91
+ if "efficientnet" in fn: arch = "EfficientNetV2"
92
+ elif "convnext" in fn: arch = "ConvNeXtV2"
93
+ elif "mobilevit" in fn: arch = "MobileViTv3"
94
+ elif "resnet50" in fn: arch = "ResNet50"
95
+ elif "resnet18" in fn: arch = "ResNet18"
96
+ elif "vgg16" in fn: arch = "VGG16"
97
+ elif "alexnet" in fn: arch = "AlexNet"
98
+ elif "inception" in fn: arch = "InceptionV3"
99
+ elif "densenet" in fn: arch = "DenseNet121"
100
+ elif "unet" in fn: arch = "UNet"
101
+ return arch, dataset
102
+
103
+ def joules_to_co2(joules):
104
+ kwh = joules / 3_600_000
105
+ return kwh * 0.475 # kg CO2e
106
+
107
+ def folder_to_phase_label(folder):
108
+ return {"test1": "SOTA Optimized", "test2": "Baseline", "test3": "EDEN Classic"}.get(folder, folder)
109
+
110
+ # ─── LOAD STATS ──────────────────────────────────────────────────────────────
111
+ with open(os.path.join(BASE_DIR, "results_summary.json")) as f:
112
+ results = json.load(f)
113
+
114
+ stats_map = {}
115
+ for r in results:
116
+ arch, dataset = parse_name(r["file"])
117
+ folder = r["folder"]
118
+ key = f"{folder}_{arch}_{dataset}"
119
+ if key not in stats_map or (r["energy"] > 0 and stats_map[key]["energy"] == 0):
120
+ stats_map[key] = r
121
+
122
+ # Build baseline map (ResNet50 from test2 per dataset)
123
+ baselines = {}
124
+ for key, v in stats_map.items():
125
+ folder, *rest = key.split("_")
126
+ arch = v.get("arch") or parse_name(v["file"])[0]
127
+ if folder == "test2":
128
+ _, ds = parse_name(v["file"])
129
+ if ds not in baselines:
130
+ baselines[ds] = v
131
+ # prefer ResNet50
132
+ if parse_name(v["file"])[0] == "ResNet50":
133
+ baselines[ds] = v
134
+
135
+ # ─── COLLECT ALL MODELS ──────────────────────────────────────────────────────
136
+ pth_files = glob.glob(os.path.join(BASE_DIR, "**/*.pth"), recursive=True)
137
+ models = []
138
+ for pth in pth_files:
139
+ rel = os.path.relpath(pth, BASE_DIR)
140
+ parts = rel.split(os.sep)
141
+ folder = parts[0]
142
+ arch, dataset = parse_name(rel)
143
+ key = f"{folder}_{arch}_{dataset}"
144
+ stat = stats_map.get(key, {})
145
+ models.append({
146
+ "pth": rel, "arch": arch, "dataset": dataset,
147
+ "folder": folder,
148
+ "accuracy": stat.get("accuracy", 0),
149
+ "energy": stat.get("energy", 0),
150
+ "time": stat.get("time", 0),
151
+ "csv": stat.get("file", "N/A"),
152
+ })
153
+
154
+ # ─── README GENERATOR ────────────────────────────────────────────────────────
155
+ def build_readme(model):
156
+ arch = model["arch"]
157
+ dataset = model["dataset"]
158
+ folder = model["folder"]
159
+ acc = model["accuracy"]
160
+ energy = model["energy"]
161
+ t = model["time"]
162
+ phase = folder_to_phase_label(folder)
163
+ ds_meta = DATASET_META.get(dataset, DATASET_META["unknown"])
164
+ co2 = joules_to_co2(energy) if energy else 0
165
+
166
+ baseline = baselines.get(dataset, {})
167
+ b_acc = baseline.get("accuracy", 0)
168
+ b_energy = baseline.get("energy", 0)
169
+ b_arch = parse_name(baseline.get("file",""))[0] if baseline else "Baseline"
170
+
171
+ # Green Delta
172
+ if b_energy and energy:
173
+ energy_savings_pct = (b_energy - energy) / b_energy * 100
174
+ d_acc = acc - b_acc
175
+ d_j = energy - b_energy
176
+ eag = d_acc / d_j if d_j != 0 else float("nan")
177
+ eag_str = f"{eag:.4e}"
178
+ savings_str = f"{energy_savings_pct:.2f}%"
179
+ acc_delta = f"{d_acc*100:+.2f}%"
180
+ else:
181
+ energy_savings_pct = 0
182
+ eag_str = "N/A"
183
+ savings_str = "N/A"
184
+ acc_delta = "N/A"
185
+
186
+ # YAML tags
187
+ arch_tag = arch.lower().replace(" ","")
188
+ yaml_co2 = f"{co2:.4f}" if co2 else "0"
189
+
190
+ yaml = f"""---
191
+ language: en
192
+ license: apache-2.0
193
+ tags:
194
+ - image-classification
195
+ - green-ai
196
+ - energy-efficiency
197
+ - computer-vision
198
+ - {arch_tag}
199
+ - eden-framework
200
+ - e2am
201
+ - sustainable-ai
202
+ datasets:
203
+ - {ds_meta['hf_name']}
204
+ metrics:
205
+ - accuracy
206
+ co2_eq_emissions:
207
+ emissions: {yaml_co2}
208
+ unit: kg
209
+ source: Estimated via CodeCarbon (grid factor 0.475 kg CO2e/kWh)
210
+ hardware_used: NVIDIA GeForce GTX 1080 Ti
211
+ dataset_info:
212
+ dataset_size: "{ds_meta['size']}"
213
+ ---"""
214
+
215
+ # Technique section
216
+ technique = PHASE_DETAIL.get(folder, "Standard training.")
217
+
218
+ # Green Delta Table
219
+ green_table = f"""| Metric | {b_arch} Baseline | **{arch} (EDEN)** | Ξ” |
220
+ |---|---|---|---|
221
+ | Accuracy | {b_acc:.4f} | **{acc:.4f}** | `{acc_delta}` |
222
+ | Total Energy (J) | {b_energy:,.0f} | **{energy:,.0f}** | `{savings_str} saved` |
223
+ | COβ‚‚ Emissions (kg) | {joules_to_co2(b_energy):.4f} | **{co2:.4f}** | β€” |
224
+ | **EAG Score** | β€” | **{eag_str}** | Ξ”Acc/Ξ”Joules |"""
225
+
226
+ cite = f"""## Cite This Research
227
+ If you use this model, please cite the **EDEN / E2AM Framework**:
228
+
229
+ ```bibtex
230
+ @misc{{eden2025,
231
+ title = {{Project EDEN: Energy-Driven Evolution of Networks}},
232
+ author = {{EDEN Research Team}},
233
+ year = {{2025}},
234
+ note = {{Hugging Face Organization: ProjectEDEN}},
235
+ url = {{https://huggingface.co/{HF_ORG}}}
236
+ }}
237
+ ```"""
238
+
239
+ readme = f"""{yaml}
240
+
241
+ # EDEN-{arch}-{dataset} οΏ½οΏ½οΏ½ *{phase}*
242
+
243
+ > **Primary KPI:** EAG (Energy-to-Accuracy Gradient) = `{eag_str}` Ξ”Acc/Ξ”Joules
244
+
245
+ ## Abstract
246
+ This model is part of **Project EDEN (Energy-Driven Evolution of Networks)**, implementing the **E2AM (Energy Efficient Advanced Model)** Framework. The goal is to shift AI benchmarking from pure accuracy to *Green SOTA* β€” maximizing predictive power per Joule consumed.
247
+
248
+ **Applied Technique:** {PHASE_MAP.get(folder, phase)}
249
+
250
+ ## Profiling Environment
251
+ | Component | Specification |
252
+ |---|---|
253
+ | **GPU** | {HARDWARE['gpu']} |
254
+ | **CPU** | {HARDWARE['cpu']} |
255
+ | **RAM** | {HARDWARE['ram']} |
256
+ | **OS** | {HARDWARE['os']} |
257
+ | **Dataset** | {dataset} β€” {ds_meta['size']} |
258
+
259
+ ## 🟒 Green Delta Table
260
+ *Comparing this model against the reference baseline (ResNet-50 equivalent)*
261
+
262
+ {green_table}
263
+
264
+ > A **positive EAG** means this model learns more per Joule than the baseline.
265
+ > A **negative EAG** indicates a trade-off where higher accuracy required more energy investment.
266
+
267
+ ## E2AM Algorithm β€” Applied Phases
268
+
269
+ {technique}
270
+
271
+ ## Training Statistics
272
+ | Metric | Value |
273
+ |---|---|
274
+ | Final Accuracy | {acc:.4f} ({acc*100:.2f}%) |
275
+ | Total Energy Consumed | {energy:,.0f} J ({energy/3_600_000:.4f} kWh) |
276
+ | Training Time | {t:,.0f} s ({t/3600:.2f} hrs) |
277
+ | Estimated COβ‚‚ | {co2:.4f} kg COβ‚‚e |
278
+ | Training Log | `{model['csv']}` |
279
+
280
+ {cite}
281
+ """
282
+ return readme
283
+
284
+ # ─── MAIN FRAMEWORK README ───────────────────────────────────────────────────
285
+ def build_main_repo_readme():
286
+ py_scripts = [os.path.relpath(p, BASE_DIR) for p in
287
+ glob.glob(os.path.join(BASE_DIR, "**/*.py"), recursive=True)
288
+ if any(k in p for k in ["Algo_", "eden_", "mobilevit_model"])]
289
+
290
+ scripts_md = "\n".join(f"- `{s}`" for s in sorted(py_scripts))
291
+
292
+ return f"""---
293
+ language: en
294
+ license: apache-2.0
295
+ tags:
296
+ - green-ai
297
+ - energy-efficiency
298
+ - e2am
299
+ - eden-framework
300
+ - sustainable-ai
301
+ - image-classification
302
+ ---
303
+
304
+ # EDEN-Core-Scripts β€” E2AM Framework Repository
305
+
306
+ > **Project EDEN (Energy-Driven Evolution of Networks)** β€” The complete algorithmic
307
+ > toolkit for Green SOTA image classification research.
308
+
309
+ ## Why EDEN?
310
+ As deep learning models scale exponentially, the carbon footprint of training has
311
+ reached unsustainable levels. Project EDEN introduces the **EAG
312
+ (Energy-to-Accuracy Gradient)** as the primary KPI β€” shifting the paradigm from
313
+ chasing raw accuracy to optimising *Green SOTA*.
314
+
315
+ ## Profiling Environment
316
+ | Component | Specification |
317
+ |---|---|
318
+ | **GPU** | {HARDWARE['gpu']} |
319
+ | **CPU** | {HARDWARE['cpu']} |
320
+ | **RAM** | {HARDWARE['ram']} |
321
+ | **OS** | {HARDWARE['os']} |
322
+
323
+ ## The E2AM Algorithm β€” All Three Phases
324
+
325
+ ### Phase 1 β€” Zero-Overhead Initialization
326
+ Dataset pre-loaded into **pinned System RAM** before training begins.
327
+ This eliminates disk I/O power spikes that would otherwise inflate energy readings
328
+ and distort EAG comparisons between architectures.
329
+
330
+ ### Phase 2 β€” Two-Stage Energy-Aware Training
331
+ 1. **Frozen Head Training** β€” Only the classification head trains for the first
332
+ `E_unfreeze` epochs. The backbone consumes no backward-pass energy.
333
+ 2. **Progressive Unfreezing** β€” At epoch `E_unfreeze`, all layers unlock.
334
+ Learning rate is decayed (`LR Γ— 0.1`) for stable fine-tuning.
335
+ 3. **Gradient Accumulation** β€” Gradients accumulated over N micro-batches,
336
+ simulating large batch sizes without VRAM spikes.
337
+ 4. **AMP (Automated Mixed Precision)** β€” `torch.cuda.amp.autocast()` halves
338
+ bandwidth per backward pass.
339
+ 5. **Sparse L1 Penalty** β€” `L_total = CrossEntropy + λ·Σ|W_trainable|`
340
+ 6. **EAG Early-Exit** β€” Training terminates if `EAG < Ξ³_EAG` for 3 consecutive
341
+ epochs, preventing wasted compute.
342
+
343
+ ### Phase 3 β€” Hardware-Aware Deployment *(Post-Training)*
344
+ - **Saliency-Energy Pruning** β€” Filters with lowest `βˆ‚Accuracy/βˆ‚W Γ· Energy_cost`
345
+ are pruned.
346
+ - **INT8 Quantization** β€” Weights converted for edge-deployment readiness.
347
+ - **Dynamic Depth Routing** β€” Simple images bypass the middle 50 % of layers
348
+ via residual skip connections, slashing inference energy.
349
+
350
+ ## EAG β€” The Expert KPI
351
+ ```
352
+ EAG = Ξ”Accuracy / Ξ”Joules
353
+ ```
354
+ EAG allows apples-to-apples comparison of any two models regardless of
355
+ architecture family. A higher EAG = more learning per unit of carbon footprint.
356
+
357
+ ## Scripts in This Repository
358
+ {scripts_md}
359
+
360
+ ## Cite This Research
361
+ ```bibtex
362
+ @misc{{eden2025,
363
+ title = {{Project EDEN: Energy-Driven Evolution of Networks}},
364
+ author = {{EDEN Research Team}},
365
+ year = {{2025}},
366
+ note = {{Hugging Face Organization: ProjectEDEN}},
367
+ url = {{https://huggingface.co/{HF_ORG}}}
368
+ }}
369
+ ```
370
+ """
371
+
372
+ # ─── OUTPUT / UPLOAD ─────────────────────────────────────────────────────────
373
+ OUT_DIR = os.path.join(BASE_DIR, "hf_readmes")
374
+ os.makedirs(OUT_DIR, exist_ok=True)
375
+
376
+ # 1. Main repo README
377
+ main_readme = build_main_repo_readme()
378
+ main_readme_path = os.path.join(OUT_DIR, "EDEN-Core-Scripts_README.md")
379
+ with open(main_readme_path, "w", encoding="utf-8") as f:
380
+ f.write(main_readme)
381
+ print("βœ“ Main repo README written.")
382
+
383
+ # 2. Per-model READMEs (deduplicated by repo name)
384
+ generated_repos = set()
385
+ repo_model_map = {} # repo_name -> (model, readme_text)
386
+
387
+ for m in models:
388
+ if m["arch"] == "unknown" or m["dataset"] == "unknown": continue
389
+ repo_name = f"EDEN-{m['arch']}-{m['dataset'].replace(' ','-')}"
390
+ # prefer highest-accuracy model per repo
391
+ if repo_name not in repo_model_map or m["accuracy"] > repo_model_map[repo_name][0]["accuracy"]:
392
+ readme_text = build_readme(m)
393
+ repo_model_map[repo_name] = (m, readme_text)
394
+
395
+ for repo_name, (m, readme_text) in repo_model_map.items():
396
+ path = os.path.join(OUT_DIR, f"{repo_name}_README.md")
397
+ with open(path, "w", encoding="utf-8") as f:
398
+ f.write(readme_text)
399
+ print(f"βœ“ {repo_name} README written.")
400
+
401
+ print(f"\n{'='*60}")
402
+ print(f"Generated {len(repo_model_map)+1} README files in: {OUT_DIR}")
403
+
404
+ if not DRY_RUN:
405
+ print("\nStarting HF upload...")
406
+
407
+ # Upload Main Repo README
408
+ try:
409
+ create_repo(repo_id=f"{HF_ORG}/EDEN-Core-Scripts", token=HF_TOKEN,
410
+ repo_type="model", exist_ok=True, private=False)
411
+ upload_file(path_or_fileobj=main_readme_path,
412
+ path_in_repo="README.md",
413
+ repo_id=f"{HF_ORG}/EDEN-Core-Scripts",
414
+ token=HF_TOKEN, repo_type="model")
415
+ # Upload all .py scripts
416
+ for py in glob.glob(os.path.join(BASE_DIR, "**/*.py"), recursive=True):
417
+ rel = os.path.relpath(py, BASE_DIR)
418
+ if any(k in rel for k in ["Algo_","eden_","mobilevit_model"]):
419
+ upload_file(path_or_fileobj=py,
420
+ path_in_repo=rel.replace("\\","/"),
421
+ repo_id=f"{HF_ORG}/EDEN-Core-Scripts",
422
+ token=HF_TOKEN, repo_type="model")
423
+ print("βœ“ Uploaded EDEN-Core-Scripts")
424
+ except Exception as e:
425
+ print(f"βœ— Core-Scripts error: {e}")
426
+
427
+ # Upload per-model repos
428
+ for repo_name, (m, readme_text) in repo_model_map.items():
429
+ try:
430
+ create_repo(repo_id=f"{HF_ORG}/{repo_name}", token=HF_TOKEN,
431
+ repo_type="model", exist_ok=True, private=False)
432
+ readme_path = os.path.join(OUT_DIR, f"{repo_name}_README.md")
433
+ upload_file(path_or_fileobj=readme_path,
434
+ path_in_repo="README.md",
435
+ repo_id=f"{HF_ORG}/{repo_name}",
436
+ token=HF_TOKEN, repo_type="model")
437
+ # Upload weights
438
+ pth_abs = os.path.join(BASE_DIR, m["pth"])
439
+ if os.path.exists(pth_abs):
440
+ upload_file(path_or_fileobj=pth_abs,
441
+ path_in_repo=os.path.basename(m["pth"]),
442
+ repo_id=f"{HF_ORG}/{repo_name}",
443
+ token=HF_TOKEN, repo_type="model")
444
+ # Upload CSV log
445
+ if m["csv"] != "N/A":
446
+ csv_abs = os.path.join(BASE_DIR, m["csv"])
447
+ if os.path.exists(csv_abs):
448
+ upload_file(path_or_fileobj=csv_abs,
449
+ path_in_repo=os.path.basename(m["csv"]),
450
+ repo_id=f"{HF_ORG}/{repo_name}",
451
+ token=HF_TOKEN, repo_type="model")
452
+ print(f"βœ“ Uploaded {repo_name}")
453
+ except Exception as e:
454
+ print(f"βœ— {repo_name} error: {e}")
455
+
456
+ print("\nAll uploads complete.")
457
+ else:
458
+ print("\n[DRY RUN] Set DRY_RUN=False to execute HF uploads.")