File size: 7,136 Bytes
f381be8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d3996f2
 
 
 
 
 
 
 
 
 
 
f381be8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f2456ca
f381be8
 
 
 
 
 
 
 
1552b5a
f2456ca
 
d3996f2
f381be8
 
 
 
 
f2456ca
 
 
 
 
 
 
 
 
 
 
ae129df
 
 
f2456ca
 
 
 
 
 
 
 
 
 
cac5a02
f2456ca
 
 
 
 
 
 
 
 
 
 
 
 
 
1552b5a
 
 
 
 
 
 
 
 
 
 
 
f381be8
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
"""
Upload all trained model artifacts to Hugging Face Hub model repository.

Usage:
    python scripts/upload_models_to_hub.py

This script:
  - Creates the HF model repo NeerajCodz/aiBatteryLifeCycle if it doesn't exist
  - Uploads artifacts/v1/ and artifacts/v2/ preserving folder structure
  - Writes a proper README / model card
"""

import os
import sys
from pathlib import Path

from huggingface_hub import HfApi, create_repo, upload_folder

# ──────────────────────────────────────────────────────────────────────────────
# Config
# ──────────────────────────────────────────────────────────────────────────────
HF_TOKEN   = os.getenv("HF_TOKEN")          # set HF_TOKEN in your shell before running
REPO_ID    = "NeerajCodz/aiBatteryLifeCycle"
REPO_TYPE  = "model"

ROOT       = Path(__file__).resolve().parent.parent   # repo root
ARTIFACTS  = ROOT / "artifacts"

MODEL_CARD = """\
---
license: mit
language:
  - en
tags:
  - battery
  - state-of-health
  - remaining-useful-life
  - time-series
  - regression
  - lstm
  - transformer
  - xgboost
  - lightgbm
  - random-forest
  - ensemble
datasets:
  - NASA-PCoE-Battery
metrics:
  - r2
  - mae
  - rmse
pipeline_tag: tabular-regression
---

# AI Battery Lifecycle β€” Model Repository

Trained model artifacts for the [aiBatteryLifeCycle](https://huggingface.co/spaces/NeerajCodz/aiBatteryLifeCycle) project.

SOH (State-of-Health) and RUL (Remaining Useful Life) prediction for lithium-ion batteries
trained on the NASA PCoE Battery Dataset.

## Repository Layout

```
artifacts/
β”œβ”€β”€ v1/
β”‚   β”œβ”€β”€ models/
β”‚   β”‚   β”œβ”€β”€ classical/   # Ridge, Lasso, ElasticNet, KNN Γ—3, SVR, XGBoost, LightGBM, RF
β”‚   β”‚   └── deep/        # Vanilla LSTM, Bi-LSTM, GRU, Attention-LSTM, TFT,
β”‚   β”‚                    # BatteryGPT, iTransformer, Physics-iTransformer,
β”‚   β”‚                    # DG-iTransformer, VAE-LSTM
β”‚   └── scalers/         # MinMax, Standard, Linear, Sequence scalers
└── v2/
    β”œβ”€β”€ models/
    β”‚   β”œβ”€β”€ classical/   # Same family + Extra Trees, Gradient Boosting, best_rul_model
    β”‚   └── deep/        # Same deep models re-trained on v2 feature set
    β”œβ”€β”€ scalers/         # Per-model feature scalers
    └── results/         # Validation JSONs
```

## Model Performance Summary (v3)

| Rank | Model | RΒ² | MAE | Family |
|------|-------|----|-----|--------|
| 1 | XGBoost | 0.9866 | 1.58 | Classical |
| 2 | GradientBoosting | 0.9860 | 1.38 | Classical |
| 3 | LightGBM | 0.9826 | 1.98 | Classical |
| 4 | RandomForest | 0.9814 | 1.83 | Classical |
| 5 | ExtraTrees | 0.9701 | 3.20 | Classical |
| 6 | TFT | 0.8751 | 3.88 | Transformer |
| 7 | Weighted Avg Ensemble | 0.8991 | 3.51 | Ensemble |

## Usage

These artifacts are automatically downloaded by the Space on startup via
`scripts/download_models.py`. You can also use them directly:

```python
from huggingface_hub import snapshot_download

local = snapshot_download(
    repo_id="NeerajCodz/aiBatteryLifeCycle",
    repo_type="model",
    local_dir="artifacts",
    token="<your-token>",   # only needed if private
)
```

## Framework

- **Classical models:** scikit-learn / XGBoost / LightGBM `.joblib`
- **Deep models (PyTorch):** `.pt` state-dicts (CPU weights)
- **Deep models (Keras):** `.keras` SavedModel format
- **Scalers:** scikit-learn `.joblib`

## Citation

```bibtex
@misc{aiBatteryLifeCycle2025,
  author  = {Neeraj},
  title   = {AI Battery Lifecycle β€” SOH/RUL Prediction},
  year    = {2025},
  url     = {https://huggingface.co/spaces/NeerajCodz/aiBatteryLifeCycle}
}
```
"""

# ──────────────────────────────────────────────────────────────────────────────

def main():
    api = HfApi(token=HF_TOKEN)

    # 1. Create repo (no-op if already exists)
    print(f"Creating / verifying repo: {REPO_ID}")
    create_repo(
        repo_id=REPO_ID,
        repo_type=REPO_TYPE,
        token=HF_TOKEN,
        exist_ok=True,
        private=False,
    )

    # 2. Upload model card
    print("Uploading README / model card...")
    api.upload_file(
        path_or_fileobj=MODEL_CARD.encode(),
        path_in_repo="README.md",
        repo_id=REPO_ID,
        repo_type=REPO_TYPE,
        commit_message="chore: update model card",
    )

    # 3. Upload each version directly at repo root: v1/ and v2/ (NOT under artifacts/)
    #    Split into one commit per subdirectory so no single commit is too large
    #    (the 100 MB random_forest.joblib would time out a combined commit).
    for version in ["v1", "v2", "v3"]:
        version_path = ARTIFACTS / version
        if not version_path.exists():
            print(f"  [skip] {version_path} does not exist")
            continue

        # Gather all subdirectories that contain files (plus version root files)
        subdirs = sorted({
            p.parent
            for p in version_path.rglob("*")
            if p.is_file()
            and ".log" not in p.suffixes
            and "__pycache__" not in p.parts
        })

        for subdir in subdirs:
            rel = subdir.relative_to(version_path)
            # Use as_posix() to ensure forward slashes on Windows
            rel_posix = rel.as_posix()
            repo_path = version if rel_posix == "." else f"{version}/{rel_posix}"

            files_in_sub = [
                f for f in subdir.iterdir()
                if f.is_file()
                and ".log" not in f.suffixes
                and f.name != ".hf_downloaded"
            ]
            if not files_in_sub:
                continue

            print(f"  Uploading {len(files_in_sub)} file(s) -> {repo_path}/")
            upload_folder(
                folder_path=str(subdir),
                path_in_repo=repo_path,
                repo_id=REPO_ID,
                repo_type=REPO_TYPE,
                token=HF_TOKEN,
                ignore_patterns=["*.log"],
                commit_message=f"feat: {repo_path}",
                run_as_future=False,
            )
            print(f"    [OK] {repo_path}/")

    # 4. Remove old artifacts/ tree from previous uploads
    print("\nCleaning up legacy artifacts/ folder in HF repo (if any)...")
    try:
        api.delete_folder(
            path_in_repo="artifacts",
            repo_id=REPO_ID,
            repo_type=REPO_TYPE,
            commit_message="chore: remove legacy artifacts/ folder (moved to repo root)",
        )
        print("  [OK] artifacts/ removed")
    except Exception as e:
        print(f"  [skip] No legacy artifacts/ to remove ({e})")

    print("\n[OK] All artifacts uploaded to", f"https://huggingface.co/{REPO_ID}")


if __name__ == "__main__":
    main()