forked from MakazhanAlpamys/Soup
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathquickstart.py
More file actions
223 lines (196 loc) · 8.54 KB
/
Copy pathquickstart.py
File metadata and controls
223 lines (196 loc) · 8.54 KB
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
218
219
220
221
222
223
"""soup quickstart — one command for a complete demo (create data + config + train)."""
from __future__ import annotations
import json
from pathlib import Path
from typing import Optional
import typer
from rich.console import Console
from rich.panel import Panel
console = Console()
# Minimal demo dataset — 20 instruction-following examples
DEMO_DATA = [
{"instruction": "What is machine learning?", "input": "",
"output": "Machine learning is a subset of AI where computers learn patterns from data."},
{"instruction": "Explain what a neural network is.", "input": "",
"output": "A neural network is a computing system inspired by biological neural networks."},
{"instruction": "What is Python?", "input": "",
"output": "Python is a high-level programming language known for its readability."},
{"instruction": "Define overfitting.", "input": "",
"output": "Overfitting is when a model learns noise in training data instead of patterns."},
{"instruction": "What is a GPU?", "input": "",
"output": "A GPU is a specialized processor designed for parallel computation."},
{"instruction": "Explain LoRA.", "input": "",
"output": "LoRA (Low-Rank Adaptation) is a technique to fine-tune large models efficiently."},
{"instruction": "What is tokenization?", "input": "",
"output": "Tokenization is the process of splitting text into smaller units called tokens."},
{"instruction": "Define transfer learning.", "input": "",
"output": "Transfer learning uses a pre-trained model as a starting point for a new task."},
{"instruction": "What is an epoch?", "input": "",
"output": "An epoch is one complete pass through the entire training dataset."},
{"instruction": "Explain gradient descent.", "input": "",
"output": "Gradient descent is an optimization algorithm that minimizes loss iteratively."},
{"instruction": "What is a loss function?", "input": "",
"output": "A loss function measures how far model predictions are from actual values."},
{"instruction": "Define batch size.", "input": "",
"output": "Batch size is the number of training samples processed before updating weights."},
{"instruction": "What is quantization?", "input": "",
"output": "Quantization reduces model precision (e.g., 32-bit to 4-bit) to save memory."},
{"instruction": "Explain attention mechanism.", "input": "",
"output": "Attention lets models focus on relevant parts of input when generating output."},
{"instruction": "What is fine-tuning?", "input": "",
"output": "Fine-tuning is training a pre-trained model on task-specific data."},
{"instruction": "Define learning rate.", "input": "",
"output": "Learning rate controls how much model weights change during each training step."},
{"instruction": "What is a transformer?", "input": "",
"output": "A transformer is a neural network architecture based on self-attention."},
{"instruction": "Explain backpropagation.", "input": "",
"output": "Backpropagation computes gradients by propagating errors backward through layers."},
{"instruction": "What is RLHF?", "input": "",
"output": "RLHF trains models using human feedback as a reward signal."},
{"instruction": "Define inference.", "input": "",
"output": "Inference is using a trained model to make predictions on new data."},
]
_DEFAULT_MODEL = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
_LOW_VRAM_MODEL = "HuggingFaceTB/SmolLM2-135M-Instruct"
_LOW_VRAM_THRESHOLD_GB = 6.0
def _pick_quickstart_model() -> tuple[str, str | None]:
"""v0.40.1 Part C / G1 — pick the demo model based on detected VRAM.
Returns ``(model_id, advisory)``. On <=6 GB GPUs (e.g. RTX 3050 4 GB)
TinyLlama 1.1B doesn't fit and crashes at step 0; auto-switch to
SmolLM2-135M (verified to train in 5 s on RTX 3050 4 GB).
"""
try:
import torch
except ImportError:
return _DEFAULT_MODEL, None
if not torch.cuda.is_available():
return _DEFAULT_MODEL, None
try:
props = torch.cuda.get_device_properties(0)
total_gb = float(getattr(props, "total_memory", 0)) / 1024**3
except (RuntimeError, OSError):
return _DEFAULT_MODEL, None
if total_gb and total_gb <= _LOW_VRAM_THRESHOLD_GB:
return (
_LOW_VRAM_MODEL,
f"Detected {total_gb:.1f} GB VRAM (≤{_LOW_VRAM_THRESHOLD_GB:.0f}) — "
f"using {_LOW_VRAM_MODEL} instead of {_DEFAULT_MODEL}.",
)
return _DEFAULT_MODEL, None
DEMO_CONFIG = """# Soup Quickstart Config — auto-generated demo
base: TinyLlama/TinyLlama-1.1B-Chat-v1.0
task: sft
data:
train: ./quickstart_data.jsonl
format: alpaca
val_split: 0.1
training:
epochs: 1
lr: 2e-4
batch_size: auto
lora:
r: 16
alpha: 32
quantization: "none"
output: ./quickstart_output
"""
def quickstart(
yes: bool = typer.Option(
False,
"--yes",
"-y",
help="Skip confirmation prompt",
),
dry_run: bool = typer.Option(
False,
"--dry-run",
help="Create data and config only, do not train",
),
output: Optional[str] = typer.Option(
None,
"--output",
"-o",
help=(
"Output directory for data, config, and run artifacts "
"(default: current directory)."
),
),
):
"""Run a complete demo: create sample data, config, and train."""
import os
from soup_cli.utils.paths import is_under_cwd
model_id, advisory = _pick_quickstart_model()
if advisory:
console.print(f"[yellow]{advisory}[/]")
# Resolve output directory (containment-checked)
if output is None:
out_dir = Path.cwd()
else:
if not is_under_cwd(output):
console.print(
f"[red]--output must stay under the current working directory; "
f"got: {output}[/]"
)
raise typer.Exit(2)
out_dir = Path(os.path.realpath(output))
out_dir.mkdir(parents=True, exist_ok=True)
console.print(
Panel(
"This will:\n"
f" 1. Create [bold]{out_dir}/quickstart_data.jsonl[/] (20 examples)\n"
f" 2. Create [bold]{out_dir}/quickstart_soup.yaml[/] config\n"
" 3. Train a tiny LoRA adapter (~1 min on GPU)\n\n"
f"Model: [bold]{model_id}[/]",
title="[bold]Soup Quickstart[/]",
)
)
if not yes and not dry_run:
confirm = typer.confirm("Continue?", default=True)
if not confirm:
console.print("[yellow]Cancelled.[/]")
raise typer.Exit()
# 1. Create demo data
data_path = out_dir / "quickstart_data.jsonl"
if data_path.exists():
console.print(f"[yellow]Data file already exists:[/] {data_path}")
else:
with open(data_path, "w", encoding="utf-8") as fh:
for entry in DEMO_DATA:
fh.write(json.dumps(entry, ensure_ascii=False) + "\n")
console.print(f"[green]Created:[/] {data_path} ({len(DEMO_DATA)} examples)")
# 2. Create demo config
config_path = out_dir / "quickstart_soup.yaml"
if config_path.exists():
console.print(f"[yellow]Config file already exists:[/] {config_path}")
else:
rendered = DEMO_CONFIG.replace(_DEFAULT_MODEL, model_id)
# When --output is set, retarget data + run dirs into that dir.
if output is not None:
rendered = rendered.replace(
"./quickstart_data.jsonl", str(data_path)
).replace("./quickstart_output", str(out_dir / "quickstart_output"))
config_path.write_text(rendered, encoding="utf-8")
console.print(f"[green]Created:[/] {config_path}")
# Also write a `soup.yaml` symlink-style alias for tools that look for it.
soup_yaml = out_dir / "soup.yaml"
if not soup_yaml.exists() and output is not None:
try:
soup_yaml.write_text(
config_path.read_text(encoding="utf-8"), encoding="utf-8"
)
except OSError:
pass
if dry_run:
console.print("\n[yellow]Dry run - files created, skipping training.[/]")
console.print(f"To train: [bold]soup train --config {config_path}[/]")
raise typer.Exit()
# 3. Train — invoke via subprocess so Typer resolves defaults properly
console.print("\n[bold]Starting training...[/]\n")
import subprocess
import sys
result = subprocess.run(
[sys.executable, "-m", "soup_cli.cli", "train", "--config", str(config_path), "--yes"],
check=False,
)
if result.returncode != 0:
raise typer.Exit(result.returncode)