File size: 7,929 Bytes
8cad2b8
 
 
 
 
 
 
 
 
 
4729446
9e74080
4a057f6
4729446
 
 
 
 
 
 
 
 
 
 
 
 
bbac272
 
 
 
 
 
 
 
16a4a9c
 
 
 
 
 
 
 
 
 
 
 
 
9e74080
 
 
 
 
 
 
 
 
 
 
 
4a057f6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# gemma-sql

# gemma-sql

> Index of experiments. Click one to open its page. Edit this page freely — the table is just Markdown.

## Experiments

| Status | Experiment |
| --- | --- |
| in-progress | [Baseline: off-the-shelf Gemma-3-270m](#/baseline-off-the-shelf-gemma-3-270m) |
| done | [Finetune Gemma-3-270m (full SFT)](#/finetune-gemma-3-270m-full-sft) |
| in-progress | [LoRA SFT](#/lora-sft) |

# Baseline: off-the-shelf Gemma-3-270m

---

### Note

`Jul 02, 2026 · 00:28 UTC`

Baseline established. Off-the-shelf google/gemma-3-270m-it, zero-shot, on a fixed 1000-example held-out subset of gretelai/synthetic_text_to_sql (test split, seed 42): execution accuracy 22.6% (175/773), exact-match 3.0%. Metric = execution accuracy: build in-memory SQLite from sql_context (CREATE+INSERT), run gold vs predicted, compare result sets order-insensitively. 227/1000 examples excluded because the gold SQL is not SQLite-executable, so we only score against verifiable ground truth. Ran on HF Jobs (l4x1), 71s. Off-the-shelf outputs are frequently garbled.

- https://huggingface.co/datasets/gretelai/synthetic_text_to_sql
- https://huggingface.co/google/gemma-3-270m-it

---

### Note

`Jul 02, 2026 · 00:36 UTC`

Few-shot baseline: google/gemma-3-270m-it with 2 in-context examples reaches 27.8% execution accuracy (exact-match 8.0%) on the same 773 scored held-out examples, up from 22.6% zero-shot. This is the strongest off-the-shelf reference to beat with finetuning.

# Finetune Gemma-3-270m (full SFT)

---

### Note

`Jul 02, 2026 · 03:30 UTC`

Finetuning complete. Full fine-tune of base google/gemma-3-270m (no LoRA) on 100k gretel train examples, 3 epochs, assistant-only loss, Gemma-3-it chat template, lr 5e-5 cosine, eff batch 32, bf16. Ran 10714s (~3h) on HF Jobs l4x1. Pushed to abidlabs/gemma-3-270m-text2sql. Metrics streamed to Trackio (abidlabs/gemma-text2sql-trackio). Eval on identical held-out set launched next.

- https://huggingface.co/abidlabs/gemma-3-270m-text2sql
- https://huggingface.co/spaces/abidlabs/gemma-text2sql-trackio

---

### Note

`Jul 02, 2026 · 06:08 UTC`

Full-SFT eval done: 73.35% execution accuracy (567/773), 40.5% exact-match, zero-shot on the identical 773-example held-out set. Massive jump over off-the-shelf baselines (22.6% zero-shot / 27.8% 2-shot). This is the target for the LoRA run to match at a fraction of the trainable params. Eval ran 3m43s on HF Jobs l4x1.

- https://huggingface.co/datasets/abidlabs/gemma-3-270m-text2sql-eval

# LoRA SFT

---

### Note

`Jul 02, 2026 · 06:09 UTC`

LoRA SFT launched on HF Jobs (l4x1, detached). Same data/template/loss/eval as full SFT — only difference is a LoRA adapter (r=16, alpha=32, dropout=0.05) on all attention + MLP projections (q,k,v,o,gate,up,down), rest frozen. lr 2e-4 (higher than full-FT's 5e-5, standard for LoRA), cosine, 3 epochs, eff batch 32, bf16, assistant-only loss, full ~100k train split. Adapter is merged into base before push so eval loads it identically. Target: match full-SFT's 73.35% exec acc at a fraction of trainable params.


````python title=train_text2sql_lora.py
# /// script
# requires-python = ">=3.10"
# dependencies = [
#   "torch",
#   "transformers>=4.56",
#   "trl>=0.12",
#   "peft>=0.13",
#   "datasets>=3.0",
#   "accelerate",
#   "trackio>=0.21.1",
#   "huggingface_hub",
# ]
# ///
"""
LoRA fine-tune of google/gemma-3-270m for natural-language -> SQL.

Identical data / chat template / loss / eval protocol as the full SFT
(train_text2sql.py) so the two are apples-to-apples; the ONLY difference is
that we train a small LoRA adapter instead of all weights. The adapter is
merged into the base model before push, so the resulting repo is a plain
CausalLM that eval_text2sql.py can load exactly like the full-FT model.

- Base weights: google/gemma-3-270m
- Tokenizer/chat template: google/gemma-3-270m-it (matches baseline eval)
- Data: gretelai/synthetic_text_to_sql train split, formatted as chat messages
- Loss: assistant-only (completion-only)
- Adapter: LoRA on attention + MLP projections
- Metrics streamed to Trackio; merged model pushed to the Hub.
"""
import argparse
from datasets import load_dataset
from peft import LoraConfig
from transformers import AutoModelForCausalLM, AutoTokenizer
from trl import SFTConfig, SFTTrainer

DATASET = "gretelai/synthetic_text_to_sql"
BASE = "google/gemma-3-270m"
CHAT_TOKENIZER = "google/gemma-3-270m-it"

SYSTEM = (
    "You are a text-to-SQL model. Given a database schema and a question, "
    "output a single valid SQLite query that answers the question. "
    "Output only the SQL query, nothing else."
)

def build_user(schema, question):
    return f"Schema:\n{schema}\n\nQuestion: {question}"

def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--hub-model-id", default="abidlabs/gemma-3-270m-text2sql-lora")
    ap.add_argument("--epochs", type=float, default=3.0)
    ap.add_argument("--lr", type=float, default=2e-4)  # LoRA likes a higher LR than full FT
    ap.add_argument("--batch-size", type=int, default=16)
    ap.add_argument("--grad-accum", type=int, default=2)
    ap.add_argument("--max-length", type=int, default=768)
    ap.add_argument("--max-train", type=int, default=0, help="0 = full split")
    ap.add_argument("--lora-r", type=int, default=16)
    ap.add_argument("--lora-alpha", type=int, default=32)
    ap.add_argument("--lora-dropout", type=float, default=0.05)
    ap.add_argument("--space-id", default="abidlabs/gemma-text2sql-trackio")
    args = ap.parse_args()

    train = load_dataset(DATASET, split="train")
    if args.max_train:
        train = train.select(range(args.max_train))
    print(f"[train] {len(train)} examples", flush=True)

    def to_messages(ex):
        return {"messages": [
            {"role": "system", "content": SYSTEM},
            {"role": "user", "content": build_user(ex["sql_context"], ex["sql_prompt"])},
            {"role": "assistant", "content": ex["sql"]},
        ]}
    train = train.map(to_messages, remove_columns=train.column_names)

    tok = AutoTokenizer.from_pretrained(CHAT_TOKENIZER)
    model = AutoModelForCausalLM.from_pretrained(BASE, dtype="bfloat16")

    peft_config = LoraConfig(
        r=args.lora_r,
        lora_alpha=args.lora_alpha,
        lora_dropout=args.lora_dropout,
        bias="none",
        task_type="CAUSAL_LM",
        target_modules=["q_proj", "k_proj", "v_proj", "o_proj",
                        "gate_proj", "up_proj", "down_proj"],
    )

    cfg = SFTConfig(
        output_dir="out",
        num_train_epochs=args.epochs,
        per_device_train_batch_size=args.batch_size,
        gradient_accumulation_steps=args.grad_accum,
        learning_rate=args.lr,
        lr_scheduler_type="cosine",
        warmup_ratio=0.03,
        logging_steps=20,
        bf16=True,
        max_length=args.max_length,
        packing=False,
        assistant_only_loss=True,
        save_strategy="epoch",
        save_total_limit=1,
        push_to_hub=False,  # push the MERGED model manually below
        report_to="trackio",
        run_name="gemma3-270m-text2sql-lora",
        project="gemma-text2sql",
        trackio_space_id=args.space_id,
    )

    trainer = SFTTrainer(
        model=model,
        args=cfg,
        train_dataset=train,
        processing_class=tok,
        peft_config=peft_config,
    )
    trainer.train()

    # Merge the LoRA adapter into the base weights so the pushed repo is a
    # plain CausalLM -> eval_text2sql.py loads it identically to the full-FT model.
    print("[train] merging LoRA adapter into base weights", flush=True)
    merged = trainer.model.merge_and_unload()
    merged.push_to_hub(args.hub_model_id)
    tok.push_to_hub(args.hub_model_id)
    print("[train] done, pushed merged model to", args.hub_model_id, flush=True)

if __name__ == "__main__":
    main()

````

- https://huggingface.co/jobs/abidlabs/6a460099fb6818a83db2fd19