Instructions to use ehzawad/ec-SFT-qwen25-7b-lora with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use ehzawad/ec-SFT-qwen25-7b-lora with PEFT:
from peft import PeftModel from transformers import AutoModelForCausalLM base_model = AutoModelForCausalLM.from_pretrained("Qwen/Qwen2.5-7B-Instruct") model = PeftModel.from_pretrained(base_model, "ehzawad/ec-SFT-qwen25-7b-lora") - Notebooks
- Google Colab
- Kaggle
EC/NID Bengali-Banglish Assistant LoRA v0.5.0
This repository contains a PEFT LoRA adapter for Qwen/Qwen2.5-7B-Instruct.
It was trained as the v0.5.0 Stage-1 dense-recall adapter for Bengali and
Banglish Bangladesh Election Commission (EC) and National ID (NID) procedural
questions.
This is an adapter-only repository. It does not include the Qwen2.5 base model weights.
Intended Use
The adapter is intended for citizen-facing EC/NID assistance: NID correction, new voter registration, lost/re-issued cards, voter-area transfer, selected overseas mission questions, and stable election-procedure explanations.
For production use, pair it with retrieval over official sources such as
services.nidw.gov.bd, ecs.gov.bd, and the 105 helpline. Static SFT weights
should not be treated as authoritative for time-sensitive facts.
Out of Scope
- Current officeholders, current voter counts, election schedules, deadlines, live fees, or other time-bound facts without retrieval.
- Non-EC/NID civic services such as passport, tax, health, or banking.
- Political persuasion, party/candidate endorsement, or voter targeting.
- Identity decisions about real people.
Training Summary
- Base model:
Qwen/Qwen2.5-7B-Instruct - Method: BF16 LoRA, no quantization
- LoRA:
r=64,alpha=128, dropout0.05 - Target modules:
q_proj,k_proj,v_proj,o_proj,gate_proj,up_proj,down_proj - Dataset version:
v0.5.0-stage1 - Rows: 32,502
- Max sequence length: 8192
- Effective batch size: 16
- Final adapter source: checkpoint/final step 2032
See dataset_manifest.json, DATASET_CARD.md, and system_prompt.txt in
this repository for the pinned data and policy metadata.
Google Colab
Paste this whole cell into a Colab GPU runtime (L4 or A100; T4 is too tight for 7B bf16). It downloads the adapter from the Hub, attaches it to Qwen/Qwen2.5-7B-Instruct, and drops you into an interactive multi-turn chat loop with token-by-token streaming. Commands: /reset clears history, /exit quits, /tokens N caps output length. Re-running the cell reuses the loaded model unless you set FORCE_RELOAD = True.
!pip -q install -U transformers peft accelerate huggingface_hub
# Colab L4/A100 often ship torchvision/bitsandbytes/torchao in a broken CUDA state.
# This adapter is bf16 LoRA inference; we do not need any of them. Removing them
# keeps the optional accelerator probes off the from_pretrained load path.
!pip -q uninstall -y torchvision bitsandbytes torchao
import inspect, json, os, sys
from pathlib import Path
from threading import Thread
import torch
from huggingface_hub import snapshot_download
ADAPTER_ID = os.environ.get("ADAPTER_ID", "ehzawad/ec-SFT-qwen25-7b-lora")
ADAPTER_REV = os.environ.get("ADAPTER_REVISION") # optional pin
ADAPTER_DIR = Path(os.environ.get("ADAPTER_DIR", "/content/adapter"))
ADAPTER_DIR.mkdir(parents=True, exist_ok=True)
snapshot_download(repo_id=ADAPTER_ID, revision=ADAPTER_REV, local_dir=str(ADAPTER_DIR))
required = ["adapter_config.json", "adapter_model.safetensors", "system_prompt.txt",
"tokenizer.json", "tokenizer_config.json"]
missing = [f for f in required if not (ADAPTER_DIR / f).is_file()]
assert not missing, f"adapter dir {ADAPTER_DIR} missing files: {missing}"
# Idempotent: skip the expensive load if model, tokenizer, SYSTEM_PROMPT already in globals.
# Set FORCE_RELOAD=True to refresh (e.g. after changing the adapter).
FORCE_RELOAD = False
if (not FORCE_RELOAD) and all(n in globals() for n in ("model", "tokenizer", "SYSTEM_PROMPT")):
print("OK reusing already-loaded model (set FORCE_RELOAD=True to refresh)")
else:
# Disable optional accelerator probes BEFORE the first transformers/peft from_pretrained call,
# otherwise a half-broken torchao C-extension on Colab can leave model params on the meta device.
import transformers.utils.import_utils as _iu
_iu.is_torchvision_available = _iu.is_torchvision_v2_available = (lambda: False)
if hasattr(_iu, "is_torchao_available"):
_iu.is_torchao_available = (lambda: False)
import peft.import_utils as _piu
for _n in ("is_bnb_available", "is_bnb_4bit_available", "is_torchao_available"):
if hasattr(_piu, _n): setattr(_piu, _n, lambda *a, **k: False)
for _m in list(sys.modules.values()):
if getattr(_m, "__name__", "").startswith("peft."):
for _n in ("is_bnb_available", "is_bnb_4bit_available", "is_torchao_available"):
if hasattr(_m, _n): setattr(_m, _n, lambda *a, **k: False)
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer
assert torch.cuda.is_available(), "No GPU found. In Colab: Runtime > Change runtime type > GPU."
print("GPU:", torch.cuda.get_device_name(0))
BASE_MODEL = (
json.loads((ADAPTER_DIR / "training_args.json").read_text()).get("base_model", "Qwen/Qwen2.5-7B-Instruct")
if (ADAPTER_DIR / "training_args.json").is_file() else "Qwen/Qwen2.5-7B-Instruct"
)
DTYPE = torch.bfloat16 if torch.cuda.is_bf16_supported() else torch.float16
tokenizer = AutoTokenizer.from_pretrained(str(ADAPTER_DIR))
if tokenizer.pad_token is None: tokenizer.pad_token = tokenizer.eos_token
assert tokenizer.vocab_size > 100000, (
f"tokenizer load looks degenerate (vocab_size={tokenizer.vocab_size}); "
f"tokenizer.json should ship in the adapter repo")
assert tokenizer.chat_template, "no chat_template loaded; expected chat_template.jinja in adapter dir"
# Transformers renamed torch_dtype -> dtype in 4.49; accept either.
_dtype_kw = "dtype" if "dtype" in inspect.signature(AutoModelForCausalLM.from_pretrained).parameters else "torch_dtype"
base = AutoModelForCausalLM.from_pretrained(BASE_MODEL, **{_dtype_kw: DTYPE},
device_map={"": 0}, attn_implementation="sdpa")
model = PeftModel.from_pretrained(base, str(ADAPTER_DIR)).eval()
model.config.use_cache = True
SYSTEM_PROMPT = (ADAPTER_DIR / "system_prompt.txt").read_text(encoding="utf-8")
from transformers import TextIteratorStreamer
def answer(question: str, max_new_tokens: int = 1024) -> str:
messages = [{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": question}]
rendered = tokenizer.apply_chat_template(messages, add_generation_prompt=True,
tokenize=True, return_tensors="pt")
input_ids = rendered.input_ids if hasattr(rendered, "input_ids") else rendered
input_ids = input_ids.to(next(model.parameters()).device)
streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True, timeout=300.0)
gen_kwargs = dict(
input_ids=input_ids, attention_mask=torch.ones_like(input_ids),
max_new_tokens=max_new_tokens, do_sample=False,
eos_token_id=tokenizer.eos_token_id,
pad_token_id=tokenizer.pad_token_id or tokenizer.eos_token_id,
use_cache=True, streamer=streamer,
)
gen_error = {}
def _gen():
try:
with torch.inference_mode():
model.generate(**gen_kwargs)
except BaseException as e:
gen_error["exc"] = e
th = Thread(target=_gen); th.start()
chunks = []
for chunk in streamer:
print(chunk, end="", flush=True)
chunks.append(chunk)
th.join()
if gen_error:
e = gen_error["exc"]
print(f"\n[generation thread raised {type(e).__name__}: {e}]")
reply = "".join(chunks).strip()
if not reply:
print("[WARN: 0 chars generated โ check tokenizer vocab_size and chat template]")
return reply
# Interactive multi-turn chat with token-by-token streaming.
# Commands: /reset (clear history), /exit (quit), /tokens N (cap output at N tokens).
MAX_POS = getattr(model.config, "max_position_embeddings", 32768)
max_new_tokens = 1024
history = []
def _to_ids(enc):
if hasattr(enc, "input_ids"): enc = enc.input_ids
if isinstance(enc, list):
enc = torch.tensor([enc] if not enc or isinstance(enc[0], int) else enc, dtype=torch.long)
if enc.dim() == 1: enc = enc.unsqueeze(0)
return enc
while True:
try:
q = input("USER> ").strip()
except (EOFError, KeyboardInterrupt):
print(); break
if not q: continue
if q in ("/exit", "/quit"): break
if q == "/reset": history.clear(); print("[cleared]"); continue
if q.startswith("/tokens"):
parts = q.split()
if len(parts) == 2 and parts[1].isdigit():
max_new_tokens = int(parts[1])
print(f"[max_new_tokens={max_new_tokens}]")
else:
print("[usage: /tokens 1024]")
continue
messages = [{"role": "system", "content": SYSTEM_PROMPT}] + history + [{"role": "user", "content": q}]
ids = _to_ids(tokenizer.apply_chat_template(messages, add_generation_prompt=True, tokenize=True, return_tensors="pt"))
ids = ids.to(next(model.parameters()).device)
cap = min(max_new_tokens, max(64, MAX_POS - ids.shape[1] - 32))
streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True, timeout=300.0)
gen_kwargs = dict(
input_ids=ids, attention_mask=torch.ones_like(ids),
max_new_tokens=cap, do_sample=False,
eos_token_id=tokenizer.eos_token_id, pad_token_id=tokenizer.pad_token_id,
use_cache=True, streamer=streamer,
)
gen_error = {}
def _gen():
try:
with torch.inference_mode():
model.generate(**gen_kwargs)
except BaseException as e:
gen_error["exc"] = e
thread = Thread(target=_gen)
thread.start()
print("BOT > ", end="", flush=True)
chunks = []
try:
for chunk in streamer:
print(chunk, end="", flush=True)
chunks.append(chunk)
except Exception as e:
print(f"\n[streamer error: {type(e).__name__}: {e}]")
thread.join()
print()
if gen_error:
e = gen_error["exc"]
print(f"[generation thread raised {type(e).__name__}: {e}]")
reply = "".join(chunks).strip()
if not reply:
print("[WARN: 0 chars generated โ check tokenizer vocab_size and chat template]")
history += [{"role": "user", "content": q}, {"role": "assistant", "content": reply}]
The answer() helper above remains available for programmatic single-shot calls if you'd rather skip the input loop. The companion notebook Stage1_v5_Inference_HF_Colab.ipynb in the source repo separates the load and chat steps into their own cells.
Caveats
This v0.5.0 adapter was intentionally trained for dense exact-answer recall. That improves recall for stable procedural answers, but it can also increase wrong-canonical answers on near-miss prompts and stale static facts unless retrieval/live lookup is used for dynamic information.
- Downloads last month
- 7