Skip to content

Commit abce003

Browse files
committed
feat: Add docs explaining engineering vs research
1 parent 07cc9a3 commit abce003

6 files changed

Lines changed: 417 additions & 71 deletions

File tree

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,13 @@ python src/train.py +experiment=long_run # apply a preset
114114

115115
Hydra saves the full resolved config alongside every run for reproducibility.
116116

117+
## Docs
118+
119+
- [docs/research.md](docs/research.md) — research workflow: MLflow, multirun sweeps, reproducibility checklist
120+
- [docs/engineering.md](docs/engineering.md) — engineering workflow: locking configs, CI, inference
121+
- [docs/experiment-tracking.md](docs/experiment-tracking.md) — TensorBoard vs MLflow vs W&B
122+
- [docs/data.md](docs/data.md) — data formats, Parquet guide, adding custom datasets
123+
117124
## License
118125

119126
MIT

data/README.md

Lines changed: 2 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,5 @@
11
# data/
22

3-
Raw and processed datasets. This directory is gitignored — do not commit data files.
3+
Raw and processed datasets. Gitignored — do not commit data files.
44

5-
## Convention
6-
7-
```
8-
data/
9-
├── cifar-10-batches-py/ # auto-downloaded by torchvision
10-
├── custom/ # your custom dataset
11-
│ ├── train/
12-
│ ├── val/
13-
│ └── test/
14-
└── raw/ # unprocessed source data
15-
```
16-
17-
## Recommended data format: Parquet (via PyArrow)
18-
19-
For tabular or structured data, use **Parquet** instead of CSV. It's faster to read, smaller on disk, and preserves column types.
20-
21-
### Saving data as Parquet
22-
23-
```python
24-
import pandas as pd
25-
26-
df = pd.DataFrame({"feature1": [1, 2, 3], "label": ["cat", "dog", "cat"]})
27-
28-
# Save — uses pyarrow backend automatically
29-
df.to_parquet("data/my_dataset.parquet")
30-
```
31-
32-
### Loading Parquet data
33-
34-
```python
35-
import pandas as pd
36-
37-
# Single file
38-
df = pd.read_parquet("data/my_dataset.parquet")
39-
40-
# Specific columns only (fast — Parquet is columnar)
41-
df = pd.read_parquet("data/my_dataset.parquet", columns=["feature1", "label"])
42-
43-
# Multiple files / partitioned dataset
44-
df = pd.read_parquet("data/my_dataset/")
45-
```
46-
47-
### Converting CSV to Parquet
48-
49-
```python
50-
df = pd.read_csv("data/raw/data.csv")
51-
df.to_parquet("data/processed/data.parquet")
52-
```
53-
54-
### Why Parquet over CSV
55-
56-
| | CSV | Parquet |
57-
|---|---|---|
58-
| Read speed | Slow (parsed line-by-line) | Fast (columnar, zero-copy) |
59-
| File size | Large (text) | Small (compressed binary) |
60-
| Column types | Lost (everything is strings) | Preserved (int, float, datetime) |
61-
| Column selection | Must read entire file | Reads only requested columns |
62-
63-
### Other supported formats
64-
65-
- **Excel** (`.xlsx`): `pd.read_excel("file.xlsx")` — requires `openpyxl`
66-
- **HDF5** (`.h5`): `pd.read_hdf("file.h5")` — requires `h5py`, common in research
67-
- **CSV**: `pd.read_csv("file.csv")` — use for small files or one-off imports
68-
69-
## Where to store data
70-
71-
- **Small datasets** (CIFAR-10, MNIST): auto-downloaded by torchvision into this folder
72-
- **Medium datasets**: download manually, place here, document the source in your experiment config
73-
- **Large datasets** (ImageNet, COCO): store on a shared drive or cluster filesystem, symlink into this folder
74-
- **Google Drive** (Colab): mount and symlink, or set `dataset.path` in your config to the Drive path
5+
See [docs/data.md](../docs/data.md) for format recommendations, Parquet usage, and how to add a custom dataset to the pipeline.

docs/data.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Data Guide
2+
3+
## Directory layout
4+
5+
```
6+
data/
7+
├── cifar-10-batches-py/ # auto-downloaded by torchvision
8+
├── custom/ # your custom dataset
9+
│ ├── train/
10+
│ ├── val/
11+
│ └── test/
12+
└── raw/ # unprocessed source data
13+
```
14+
15+
`data/` is gitignored. Never commit data files.
16+
17+
## Where to get data
18+
19+
- **Small standard datasets** (CIFAR-10, MNIST, ImageNet subsets) — auto-downloaded by torchvision on first run
20+
- **Custom datasets** — download manually, place in `data/`, document the source in your dataset config as a comment
21+
- **Large datasets** (ImageNet, COCO) — store on a shared drive or cluster filesystem, set `dataset.path` in your config to that location
22+
23+
## Recommended format: Parquet
24+
25+
For tabular or structured data, use Parquet instead of CSV. It's faster, smaller, and preserves column types.
26+
27+
### Save
28+
29+
```python
30+
import pandas as pd
31+
32+
df = pd.DataFrame({"feature1": [1, 2, 3], "label": ["cat", "dog", "cat"]})
33+
df.to_parquet("data/my_dataset.parquet")
34+
```
35+
36+
### Load
37+
38+
```python
39+
import pandas as pd
40+
41+
# Single file
42+
df = pd.read_parquet("data/my_dataset.parquet")
43+
44+
# Specific columns only (fast — Parquet is columnar)
45+
df = pd.read_parquet("data/my_dataset.parquet", columns=["feature1", "label"])
46+
47+
# Partitioned dataset (multiple files in a folder)
48+
df = pd.read_parquet("data/my_dataset/")
49+
```
50+
51+
### Convert from CSV
52+
53+
```python
54+
df = pd.read_csv("data/raw/data.csv")
55+
df.to_parquet("data/processed/data.parquet")
56+
```
57+
58+
### Why Parquet over CSV
59+
60+
| | CSV | Parquet |
61+
|---|---|---|
62+
| Read speed | Slow (text parsing) | Fast (columnar, binary) |
63+
| File size | Large | Small (compressed) |
64+
| Column types | Lost (all strings) | Preserved |
65+
| Column selection | Reads entire file | Reads only what you ask for |
66+
67+
## Other formats
68+
69+
| Format | Read | When to use |
70+
|---|---|---|
71+
| Excel `.xlsx` | `pd.read_excel("file.xlsx")` | Data from non-technical collaborators |
72+
| HDF5 `.h5` | `pd.read_hdf("file.h5")` | Large arrays, research datasets |
73+
| NumPy `.npy` / `.npz` | `np.load("file.npy")` | Pre-processed tensor data |
74+
| CSV | `pd.read_csv("file.csv")` | Small files, one-off imports only |
75+
76+
## Adding a custom dataset to the training pipeline
77+
78+
1. Create `configs/dataset/my_dataset.yaml`:
79+
80+
```yaml
81+
name: my_dataset
82+
path: ./data/my_dataset
83+
num_classes: 5
84+
augment: true
85+
```
86+
87+
2. Add a builder in `src/data.py`:
88+
89+
```python
90+
def _build_my_dataset(cfg):
91+
# Load your data, return (train_dataset, val_dataset)
92+
df = pd.read_parquet(cfg.path)
93+
# ... wrap in a torch Dataset
94+
return train_ds, val_ds
95+
```
96+
97+
3. Register it in `build_dataset()`:
98+
99+
```python
100+
if cfg.name == "my_dataset":
101+
return _build_my_dataset(cfg)
102+
```
103+
104+
4. Train:
105+
106+
```bash
107+
python src/train.py dataset=my_dataset
108+
```

docs/engineering.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Engineering Projects
2+
3+
Engineering projects are pipeline-driven. You have a specific model to build, a dataset to handle, and something to deliver — a working classifier, a detection system, an inference API. This guide covers how to use the seed for that workflow.
4+
5+
## What makes it engineering
6+
7+
- You have a deliverable: "a model that classifies X with >90% accuracy"
8+
- You run a handful of experiments to tune, then lock the config
9+
- Reliability and reproducibility of the pipeline matter more than exploring many approaches
10+
- Output is a deployed model, an API, or an integration into a larger system
11+
12+
## Recommended setup
13+
14+
The defaults are already engineering-appropriate. TensorBoard is on, no experiment tracking overhead:
15+
16+
```bash
17+
python src/train.py
18+
```
19+
20+
Once you've settled on a config, lock it in an experiment file so anyone can reproduce the exact run:
21+
22+
```yaml
23+
# configs/experiment/production.yaml
24+
# @package _global_
25+
training:
26+
epochs: 30
27+
lr: 0.001
28+
batch_size: 64
29+
seed: 42
30+
31+
logging:
32+
tensorboard: true
33+
mlflow: false
34+
```
35+
36+
```bash
37+
python src/train.py +experiment=production
38+
```
39+
40+
## Typical workflow
41+
42+
1. **Explore** — run a few experiments with different LRs/architectures to find what works
43+
2. **Lock** — commit your best experiment config to `configs/experiment/`
44+
3. **Train final model** — run with the locked config, save the checkpoint
45+
4. **Share weights** — push to HuggingFace Hub (see [../checkpoints/README.md](../checkpoints/README.md))
46+
5. **Evaluate** — run `src/evaluate.py` and log the final metrics
47+
48+
## Adding your data pipeline
49+
50+
Replace CIFAR-10 with your dataset (see README "Adding a dataset"). For engineering projects, data preprocessing is often the most important part — invest time here:
51+
52+
- Clean and validate inputs before training
53+
- Use Parquet for tabular data (see [data.md](data.md))
54+
- Add a data validation step before your `build_dataset()` function
55+
56+
## CI / automated testing
57+
58+
The seed ships with a basic GitHub Actions CI that lints and runs smoke tests. For engineering projects, extend it to include:
59+
60+
```yaml
61+
# .github/workflows/ci.yml — add after existing jobs
62+
integration-test:
63+
runs-on: ubuntu-latest
64+
steps:
65+
- uses: actions/checkout@v4
66+
- uses: actions/setup-python@v5
67+
with:
68+
python-version: "3.12"
69+
- run: pip install torch torchvision --index-url https://download.pytorch.org/whl/cpu
70+
- run: pip install hydra-core omegaconf pytest numpy
71+
# Run a real 1-epoch training to catch pipeline regressions
72+
- run: python src/train.py training.epochs=1 training.device=cpu
73+
```
74+
75+
## Inference
76+
77+
After training, evaluate the model on a held-out test set:
78+
79+
```bash
80+
python src/evaluate.py checkpoint=outputs/<date>/<time>/best_model.pt
81+
```
82+
83+
For serving predictions in a larger system, load the checkpoint directly:
84+
85+
```python
86+
from src.models import build_model
87+
from src.utils import load_checkpoint
88+
from omegaconf import OmegaConf
89+
90+
cfg = OmegaConf.load("outputs/<date>/<time>/config.yaml")
91+
model = build_model(cfg.model)
92+
load_checkpoint("checkpoints/best_model.pt", model)
93+
model.eval()
94+
```

0 commit comments

Comments
 (0)