How to use from the
Use from the
PEFT library
from peft import PeftModel
from transformers import AutoModelForCausalLM

base_model = AutoModelForCausalLM.from_pretrained("unsloth/qwen2.5-coder-3b-instruct-bnb-4bit")
model = PeftModel.from_pretrained(base_model, "qrizan/nl2sql-id-qlora-3b")

NL2SQL-ID QLoRA - Indonesian Text-to-SQL (3B)

Model QLoRA adapter (r=16) hasil fine-tuning Qwen2.5-Coder-3B-Instruct untuk menerjemahkan pertanyaan bisnis Bahasa Indonesia menjadi SQL.

Proyek eksperimen. Model dilatih khusus untuk satu skema e-commerce 5 tabel (customers, orders, order_items, products, payments). Bisa dipakai di database lain hanya jika skemanya persis sama. Untuk skema berbeda, perlu fine-tuning ulang dengan data baru.


Hasil

3-way comparison pada 365 contoh eval_dev (nilai yang belum pernah dilihat model):

Sistem Exec Acc Valid SQL Error dominan
Base (Qwen2.5-Coder-3B, zero-shot) 0.27% 3.0% invalid_sql (354)
gpt-4o-mini (zero-shot) 48.5% 100% wrong_join (99)
Model ini 97.26% 100% other (10)

Final pada eval_test (365 held-out, tidak pernah disentuh selama pengembangan):

Metrik Nilai
Execution Acc 91.78%
Valid SQL 100%
Gap vs dev 5.48pp (generalisasi baik)

Skema Database

Model dilatih dan hanya bekerja untuk skema 5 tabel berikut. Prompt harus selalu menyertakan DDL ini:

Tabel Kolom
customers id, name, city
orders id, customer_id, created_at, status
order_items id, order_id, product_id, qty, price
products id, name, category, price
payments id, order_id, amount, paid_at, method

Query yang didukung: SELECT, JOIN (max 4 tabel), WHERE, GROUP BY, ORDER BY, SUM/COUNT/AVG, HAVING, filter tanggal (strftime, >= AND <), LIMIT.


Cara Pakai

Load dari HF Hub

from unsloth import FastLanguageModel

model, tokenizer = FastLanguageModel.from_pretrained(
    model_name="qrizan/nl2sql-id-qlora-3b",
    max_seq_length=768,
    load_in_4bit=True,
)
FastLanguageModel.for_inference(model)

Inference (self-contained, tanpa dependensi proyek)

import re

# Salin skema database anda ke sini
SCHEMA_TEXT = """
customers(id, name, city)
orders(id, customer_id, created_at, status)
order_items(id, order_id, product_id, qty, price)
products(id, name, category, price)
payments(id, order_id, amount, paid_at, method)
"""

pertanyaan = "berapa total penjualan kategori elektronik bulan Januari 2025?"

# System prompt harus persis seperti saat training (src/prompts.py::get_system_prompt)
system = (
    "Anda adalah asisten SQL yang menerjemahkan pertanyaan bisnis "
    "Bahasa Indonesia menjadi query SQLite.\n\n"
    f"Skema database:\n{SCHEMA_TEXT}\n\n"
    "Aturan:\n"
    "- Gunakan tabel dan kolom sesuai skema di atas.\n"
    "- Tulis reasoning singkat dalam tag <think>...</think>:\n"
    "  Tabel: (tabel yang dipakai)\n"
    "  Join: (kondisi join)\n"
    "  Filter: (kondisi WHERE)\n"
    "  Agregasi: (fungsi agregasi, atau '-' jika tidak ada)\n"
    "- Setelah </think>, tulis SQL tanpa markdown fence.\n"
    "- Hanya SELECT, tanpa INSERT/UPDATE/DELETE."
)
messages = [
    {"role": "system", "content": system},
    {"role": "user", "content": pertanyaan},
]

prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)

outputs = model.generate(**inputs, max_new_tokens=512, do_sample=False)
response = tokenizer.decode(outputs[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)

# Ekstrak SQL setelah </think>
m = re.search(r"</think>\s*(.*)", response, re.DOTALL | re.IGNORECASE)
sql = m.group(1).strip() if m else response.strip()
# Bersihkan markdown fence jika ada
sql = re.sub(r"```(?:sql)?\s*", "", sql, flags=re.IGNORECASE).replace("```", "").strip()

print(sql)
# SELECT SUM(oi.qty * oi.price) FROM order_items oi
#   JOIN products p ON oi.product_id = p.id
#   JOIN orders o ON oi.order_id = o.id
# WHERE p.category = 'elektronik'
#   AND strftime('%Y-%m', o.created_at) = '2025-01'

Format Output

Model menghasilkan reasoning chain diikuti SQL:

<think>
Tabel: order_items, products, orders
Join: oi.product_id = p.id, oi.order_id = o.id
Filter: p.category = 'elektronik', strftime('%Y-%m', o.created_at) = '2025-01'
Agregasi: SUM(qty * price)
</think>
SELECT SUM(oi.qty * oi.price) ...

SQL diekstrak setelah </think>. Baris SCHEMA_TEXT bisa diganti dengan DDL database anda - asal strukturnya sama dengan 5 tabel di atas.


Training

Parameter Nilai
Model base Qwen2.5-Coder-3B-Instruct (4-bit)
Metode QLoRA (r=16, alpha=16, dropout=0.05)
Data train 820 contoh (pool A: elektronik/fashion/makanan/furnitur, Jan-Jun 2025)
Epoch 5
Learning rate 2e-4
Batch size 16 (2 x grad_accum 8)
GPU Colab T4 (~15 menit)
Trainable params 29.9M (0.96%)
Adapter size 115 MB

Train dan eval menggunakan nilai berbeda (pool A vs pool B: kategori, bulan, kota) untuk menguji generalisasi, bukan hafalan. Hasil dev 97.26%, test held-out 91.78%.


Batasan

  • Skema spesifik. Hanya untuk 5 tabel di atas. Skema berbeda perlu fine-tuning ulang.
  • Query SELECT saja - tidak mendukung INSERT/UPDATE/DELETE
  • Bahasa Indonesia dengan kosakata bisnis, tidak diuji pada bahasa lain
  • Maksimum ~768 token input

Lisensi

Apache 2.0 - bebas dipakai, dimodifikasi, didistribusikan.

Kode Lengkap

Pipeline training, evaluasi, demo: github.com/qrizan/llm-posttraining-experiments

Downloads last month
30
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support