Instructions to use visethchapman/ercot-text2sql-qwen-1.5b-lora with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use visethchapman/ercot-text2sql-qwen-1.5b-lora with PEFT:
from peft import PeftModel from transformers import AutoModelForCausalLM base_model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-Coder-1.5B-Instruct") model = PeftModel.from_pretrained(base_model, "visethchapman/ercot-text2sql-qwen-1.5b-lora") - Notebooks
- Google Colab
- Kaggle
ERCOT Text-to-SQL β Qwen2.5-Coder-1.5B LoRA
LoRA fine-tune of Qwen/Qwen2.5-Coder-1.5B-Instruct for natural-language-to-SQL over an ERCOT electricity-demand + Houston weather Postgres schema.
Companion to text2sql-finetune (training code + lessons) and energy-text2sql (baseline multi-agent + eval harness).
What it does
Given an English question about ERCOT hourly electricity demand or Houston weather, generates the Postgres SQL to answer it.
Example:
Q: What was peak hourly ERCOT demand in 2024?
SQL:
SELECT MAX(value) AS peak_demand FROM eia.demand WHERE region = 'ERCO' AND EXTRACT(YEAR FROM period) = 2024;
Training details
| Base | Qwen/Qwen2.5-Coder-1.5B-Instruct |
| Method | LoRA (fp16, no 4-bit quantization) |
| LoRA rank / alpha / dropout | 16 / 32 / 0.05 |
| Target modules | q_proj, k_proj, v_proj, o_proj, gate_proj, up_proj, down_proj |
| Trainable params | 18.5M / 1.56B (1.18%) |
| Training data | 280 synthetic Q/SQL pairs |
| Epochs | 3 |
| Effective batch size | 16 (per-device 2 Γ grad-accum 8) |
| Learning rate | 2e-4, cosine, 3% warmup |
| Max seq length | 1024 |
| Hardware | Kaggle T4 (free tier) |
| Wall clock | ~4.5 min |
| Final train / val loss | 0.11 / 0.11 |
| Val mean token accuracy | 97% |
How to use
The model expects the schema in the system prompt β it wasn't trained to memorize tables, only to map (schema + question) β SQL. A stub prompt causes table hallucination.
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import PeftModel
BASE = "Qwen/Qwen2.5-Coder-1.5B-Instruct"
ADAPTER = "visethchapman/ercot-text2sql-qwen-1.5b-lora"
tokenizer = AutoTokenizer.from_pretrained(BASE)
model = AutoModelForCausalLM.from_pretrained(BASE, torch_dtype=torch.float16, device_map="auto")
model = PeftModel.from_pretrained(model, ADAPTER)
SYSTEM = """You are a Postgres SQL expert for ERCOT electricity-demand and Houston weather data.
Schema:
eia.demand(region, period, value, value_units) β region='ERCO'; period in UTC; value in MWh
noaa.daily_weather(station_id, obs_date, tmax_c, tmin_c, prcp_mm, awnd_ms) β Houston station; obs_date is local date
noaa.stations(station_id, name, state, nearest_eia_region)
Notes: All demand is UTC. Houston weather is local date. For joins,
cast period to local date: (period AT TIME ZONE 'America/Chicago')::date
Return ONLY valid Postgres SQL. No explanation, no markdown fences."""
msgs = [
{"role": "system", "content": SYSTEM},
{"role": "user", "content": "What was peak hourly ERCOT demand in 2024?"},
]
inputs = tokenizer.apply_chat_template(
msgs, return_tensors="pt", add_generation_prompt=True, return_dict=True,
).to(model.device)
out = model.generate(**inputs, max_new_tokens=256, do_sample=False)
print(tokenizer.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True))
Eval results
Evaluated on the 12-question held-out eval set from
energy-text2sql
(result-row equivalence, sort-insensitive, float-tolerant):
| Model | Correct | Cost / run | Avg latency |
|---|---|---|---|
| Qwen2.5-Coder-1.5B (raw base, no fine-tune) | 2/12 (17%) | $0.00 | 5.26s |
| Qwen2.5-Coder-1.5B + LoRA (this model) | 6/12 (50%) | $0.00 | 3.12s |
| Claude Sonnet 4.5 (single-call baseline) | 12/12 (100%) | ~$0.05 | 4.6s |
| Claude Sonnet 4.5 multi-agent (LangGraph 4-node) | 12/12 (100%) | ~$0.10 | 9.1s |
Fine-tuning tripled the base-model score (2 β 6 correct) at zero incremental inference cost. The model handles simple aggregations and filters (peaks, totals, summer averages) but still fails on subtle SQL rules (GROUP BY with non-aggregated columns, alias-in-ORDER-BY) and cross-domain joins requiring TZ casts. Not a Claude replacement β a smaller fine-tuned model that closes half the gap at $0 marginal cost.
Training data
- 500 raw Q/SQL pairs generated by Claude Sonnet 4.5 against the ERCOT schema
- β3 dropped in validation (SQL didn't execute or returned empty)
- β22 dropped as eval-set leaks (fuzzy paraphrase of held-out eval questions)
- β165 dropped as intra-set duplicates (same SQL skeleton, different literals)
- 310 unique β 280 train / 15 val / 15 test
Full generation + dedupe pipeline in text2sql-finetune.
Limitations
- Small model, narrow domain. Fine-tuned on 280 pairs against one Postgres schema. Won't generalize to other schemas.
- Claude Sonnet 4.5 still wins on this task. It scores 12/12 on the held-out eval; this fine-tuned model is not a Claude replacement. The value here is proximity β how close a 1.5B fine-tuned model can get to Claude Sonnet 4.5.
- Synthetic data biases. The training pairs were generated by Claude, so the model inherits Claude's SQL style and blind spots.
- Schema must be in the system prompt. See the
How to useblock β a stub prompt makes the model hallucinate tables.
Portfolio context
Built as a self-study project to demonstrate the full LLM fine-tuning workflow: synthetic dataset generation, deduplication (including test-set leak check), LoRA/PEFT on HuggingFace TRL, and evaluation against an existing multi-agent baseline. The repo README has the "What I learned" section β Claude data redundancy, GPU compatibility, QLoRA-vs-LoRA trade-off.
License
Apache-2.0. Base model retains its own Qwen2.5 license.
- Downloads last month
- 54
Model tree for visethchapman/ercot-text2sql-qwen-1.5b-lora
Base model
Qwen/Qwen2.5-1.5B