v1.5b faz6: cok-turlu {messages} maskeleme (encode_messages/encode_any) + base checkpoints_350m namespace + max_len 2048 (context)
Browse files- code/kod/faz6_sft.py +58 -12
code/kod/faz6_sft.py
CHANGED
|
@@ -2,11 +2,14 @@
|
|
| 2 |
Faz 6 — SFT (instruction fine-tuning) iskeleti — SmartCore V1.
|
| 3 |
|
| 4 |
Base modeli (kdirgul/smartcore-v1 son ckpt) instruction verisiyle fine-tune eder.
|
| 5 |
-
Veri: JSONL
|
| 6 |
-
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
-
Ortam: Colab GPU + mamba-
|
| 9 |
-
Yerelde test edilebilir
|
| 10 |
|
| 11 |
Kullanım:
|
| 12 |
HF_TOKEN=hf_xxx python faz6_sft.py --data sft.jsonl --epochs 3 --lr 2e-5 \
|
|
@@ -135,6 +138,43 @@ def encode_example(sp, ex, max_len, eos_id):
|
|
| 135 |
return ids, labels
|
| 136 |
|
| 137 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
def collate(batch, pad_id):
|
| 139 |
"""Sağdan pad. input_ids pad_id ile, labels -100 ile doldurulur."""
|
| 140 |
maxlen = max(len(ids) for ids, _ in batch)
|
|
@@ -169,17 +209,19 @@ def load_tok(token):
|
|
| 169 |
return spm.SentencePieceProcessor(model_file=p)
|
| 170 |
|
| 171 |
|
| 172 |
-
def base_ckpt(spec, token):
|
| 173 |
if spec and spec != "latest_hf":
|
| 174 |
if os.path.exists(spec):
|
| 175 |
return spec
|
| 176 |
from huggingface_hub import hf_hub_download # HF yolu (örn sft/epoch_2/ckpt.pt) → devam SFT
|
| 177 |
print(f"[base] HF: {spec}", flush=True)
|
| 178 |
return hf_hub_download("kdirgul/smartcore-v1", spec, repo_type="model", token=token)
|
| 179 |
-
from huggingface_hub import HfApi, hf_hub_download
|
| 180 |
api = HfApi(token=token)
|
| 181 |
fs = [f for f in api.list_repo_files("kdirgul/smartcore-v1", repo_type="model")
|
| 182 |
-
if f.startswith("
|
|
|
|
|
|
|
| 183 |
latest = max(fs); print(f"[base] {latest}", flush=True)
|
| 184 |
return hf_hub_download("kdirgul/smartcore-v1", latest, repo_type="model", token=token)
|
| 185 |
|
|
@@ -192,7 +234,7 @@ def load_examples(path):
|
|
| 192 |
if not line:
|
| 193 |
continue
|
| 194 |
ex = json.loads(line)
|
| 195 |
-
if ex.get("instruction") and ex.get("output"):
|
| 196 |
rows.append(ex)
|
| 197 |
return rows
|
| 198 |
|
|
@@ -226,7 +268,9 @@ def load_replay(token, n, max_len, seed):
|
|
| 226 |
def main():
|
| 227 |
ap = argparse.ArgumentParser()
|
| 228 |
ap.add_argument("--data", required=True, help="instruction JSONL yolu")
|
| 229 |
-
ap.add_argument("--base", default="latest_hf", help="latest_hf |
|
|
|
|
|
|
|
| 230 |
ap.add_argument("--epochs", type=int, default=3)
|
| 231 |
ap.add_argument("--lr", type=float, default=2e-5)
|
| 232 |
ap.add_argument("--micro_batch", type=int, default=8)
|
|
@@ -257,7 +301,7 @@ def main():
|
|
| 257 |
pass
|
| 258 |
|
| 259 |
sp = load_tok(token); eos_id = sp.eos_id(); pad_id = max(sp.pad_id(), 0)
|
| 260 |
-
st = torch.load(base_ckpt(args.base, token), map_location="cpu")
|
| 261 |
cfg = st["cfg"]
|
| 262 |
model = HybridLM(cfg, device=dev, dtype=torch.bfloat16)
|
| 263 |
model.load_state_dict(st["model"], strict=False); model.train()
|
|
@@ -267,8 +311,10 @@ def main():
|
|
| 267 |
random.shuffle(rows)
|
| 268 |
n_val = max(1, int(len(rows) * args.val_frac)) if args.val_frac > 0 else 0
|
| 269 |
val_rows, train_rows = rows[:n_val], rows[n_val:]
|
| 270 |
-
enc = [
|
| 271 |
-
val_enc = [
|
|
|
|
|
|
|
| 272 |
n_sft = len(enc)
|
| 273 |
if args.replay_frac > 0:
|
| 274 |
n_replay = int(n_sft * args.replay_frac / (1 - args.replay_frac))
|
|
|
|
| 2 |
Faz 6 — SFT (instruction fine-tuning) iskeleti — SmartCore V1.
|
| 3 |
|
| 4 |
Base modeli (kdirgul/smartcore-v1 son ckpt) instruction verisiyle fine-tune eder.
|
| 5 |
+
Veri: JSONL. İKİ format desteklenir (encode_any tespit eder):
|
| 6 |
+
• tek-tur (v1): {"instruction": str, "input": str(ops), "output": str} → ### Talimat/### Yanıt
|
| 7 |
+
• çok-turlu (v1.5b): {"messages": [{"role","content"},...]} → ### Sistem/### Kullanıcı/### Asistan
|
| 8 |
+
KRİTİK: loss SADECE asistan/yanıt tokenlerinde (+eos) — prompt/kullanıcı -100 ile maskelenir.
|
| 9 |
+
NOT: --max_len ≤ pretraining seq_len (2048) tutulmalı (GQA-RoPE extrapolation'ı önle).
|
| 10 |
|
| 11 |
+
Ortam: Colab GPU + resmî mamba-ssm (wheel-cache). Model tanımı gömülü (import bağımlılığı yok).
|
| 12 |
+
Yerelde test edilebilir (mamba-ssm'siz): build_prompt / encode_example / encode_messages / collate / lr_at.
|
| 13 |
|
| 14 |
Kullanım:
|
| 15 |
HF_TOKEN=hf_xxx python faz6_sft.py --data sft.jsonl --epochs 3 --lr 2e-5 \
|
|
|
|
| 138 |
return ids, labels
|
| 139 |
|
| 140 |
|
| 141 |
+
# çok-turlu şablon — faz6_prep_v15b.py render() ile BİREBİR AYNI olmalı (SP özel-token YOK)
|
| 142 |
+
SYS_PFX, USER_PFX, ASST_PFX = "### Sistem:\n", "### Kullanıcı:\n", "### Asistan:\n"
|
| 143 |
+
_PFX = {"system": SYS_PFX, "user": USER_PFX, "assistant": ASST_PFX}
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
def encode_messages(sp, ex, max_len, eos_id):
|
| 147 |
+
"""Çok-turlu {messages} → (ids, labels). SADECE asistan içeriği + her turun eos'u öğrenilir;
|
| 148 |
+
sistem/kullanıcı turları + tüm prefix'ler + ayraçlar -100 (maskeli). Uzunsa SOLDAN kırpılır
|
| 149 |
+
(son yanıt korunur). Şablon faz6_prep_v15b.render() ile aynı (segment-segment tokenize)."""
|
| 150 |
+
msgs = ex["messages"]
|
| 151 |
+
ids, labels = [], []
|
| 152 |
+
for i, m in enumerate(msgs):
|
| 153 |
+
role = m["role"]; content = (m.get("content") or "").strip()
|
| 154 |
+
prefix = _PFX[role]
|
| 155 |
+
sep = "\n\n" if i < len(msgs) - 1 else "" # turlar \n\n ile birleşir (render ile aynı)
|
| 156 |
+
if role == "assistant":
|
| 157 |
+
p = sp.encode(prefix, out_type=int) # "### Asistan:\n" → maskeli (inference'ta biz veririz)
|
| 158 |
+
c = sp.encode(content, out_type=int) + [eos_id] # içerik + eos → ÖĞRENİLİR
|
| 159 |
+
s = sp.encode(sep, out_type=int) if sep else []
|
| 160 |
+
ids += p + c + s
|
| 161 |
+
labels += [-100] * len(p) + c + [-100] * len(s)
|
| 162 |
+
else: # system/user → tümü maskeli
|
| 163 |
+
seg = sp.encode(prefix + content + sep, out_type=int)
|
| 164 |
+
ids += seg
|
| 165 |
+
labels += [-100] * len(seg)
|
| 166 |
+
if len(ids) > max_len:
|
| 167 |
+
ids, labels = ids[-max_len:], labels[-max_len:] # son yanıtı koru
|
| 168 |
+
return ids, labels
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
def encode_any(sp, ex, max_len, eos_id):
|
| 172 |
+
"""Format tespiti: {messages}→çok-turlu; {instruction,output}→tek-tur (v1 uyumlu)."""
|
| 173 |
+
if ex.get("messages"):
|
| 174 |
+
return encode_messages(sp, ex, max_len, eos_id)
|
| 175 |
+
return encode_example(sp, ex, max_len, eos_id)
|
| 176 |
+
|
| 177 |
+
|
| 178 |
def collate(batch, pad_id):
|
| 179 |
"""Sağdan pad. input_ids pad_id ile, labels -100 ile doldurulur."""
|
| 180 |
maxlen = max(len(ids) for ids, _ in batch)
|
|
|
|
| 209 |
return spm.SentencePieceProcessor(model_file=p)
|
| 210 |
|
| 211 |
|
| 212 |
+
def base_ckpt(spec, token, subdir="checkpoints_350m"):
|
| 213 |
if spec and spec != "latest_hf":
|
| 214 |
if os.path.exists(spec):
|
| 215 |
return spec
|
| 216 |
from huggingface_hub import hf_hub_download # HF yolu (örn sft/epoch_2/ckpt.pt) → devam SFT
|
| 217 |
print(f"[base] HF: {spec}", flush=True)
|
| 218 |
return hf_hub_download("kdirgul/smartcore-v1", spec, repo_type="model", token=token)
|
| 219 |
+
from huggingface_hub import HfApi, hf_hub_download # latest_hf → subdir'deki son pretrain ckpt (v1.5b=checkpoints_350m)
|
| 220 |
api = HfApi(token=token)
|
| 221 |
fs = [f for f in api.list_repo_files("kdirgul/smartcore-v1", repo_type="model")
|
| 222 |
+
if f.startswith(f"{subdir}/step_") and f.endswith("ckpt.pt")]
|
| 223 |
+
if not fs:
|
| 224 |
+
sys.exit(f"[hata] '{subdir}/' altında pretrain ckpt yok. (v1.0 için --base_subdir checkpoints)")
|
| 225 |
latest = max(fs); print(f"[base] {latest}", flush=True)
|
| 226 |
return hf_hub_download("kdirgul/smartcore-v1", latest, repo_type="model", token=token)
|
| 227 |
|
|
|
|
| 234 |
if not line:
|
| 235 |
continue
|
| 236 |
ex = json.loads(line)
|
| 237 |
+
if ex.get("messages") or (ex.get("instruction") and ex.get("output")):
|
| 238 |
rows.append(ex)
|
| 239 |
return rows
|
| 240 |
|
|
|
|
| 268 |
def main():
|
| 269 |
ap = argparse.ArgumentParser()
|
| 270 |
ap.add_argument("--data", required=True, help="instruction JSONL yolu")
|
| 271 |
+
ap.add_argument("--base", default="latest_hf", help="latest_hf | HF yolu (sft/epoch_2/ckpt.pt) | yerel .pt")
|
| 272 |
+
ap.add_argument("--base_subdir", default="checkpoints_350m",
|
| 273 |
+
help="latest_hf namespace (v1.5b=checkpoints_350m, v1.0=checkpoints)")
|
| 274 |
ap.add_argument("--epochs", type=int, default=3)
|
| 275 |
ap.add_argument("--lr", type=float, default=2e-5)
|
| 276 |
ap.add_argument("--micro_batch", type=int, default=8)
|
|
|
|
| 301 |
pass
|
| 302 |
|
| 303 |
sp = load_tok(token); eos_id = sp.eos_id(); pad_id = max(sp.pad_id(), 0)
|
| 304 |
+
st = torch.load(base_ckpt(args.base, token, args.base_subdir), map_location="cpu", weights_only=False)
|
| 305 |
cfg = st["cfg"]
|
| 306 |
model = HybridLM(cfg, device=dev, dtype=torch.bfloat16)
|
| 307 |
model.load_state_dict(st["model"], strict=False); model.train()
|
|
|
|
| 311 |
random.shuffle(rows)
|
| 312 |
n_val = max(1, int(len(rows) * args.val_frac)) if args.val_frac > 0 else 0
|
| 313 |
val_rows, train_rows = rows[:n_val], rows[n_val:]
|
| 314 |
+
enc = [encode_any(sp, ex, args.max_len, eos_id) for ex in train_rows]
|
| 315 |
+
val_enc = [encode_any(sp, ex, args.max_len, eos_id) for ex in val_rows]
|
| 316 |
+
n_multi = sum(1 for ex in train_rows if ex.get("messages"))
|
| 317 |
+
print(f"[format] çok-turlu(messages)={n_multi} | tek-tur(instruction)={len(train_rows)-n_multi}", flush=True)
|
| 318 |
n_sft = len(enc)
|
| 319 |
if args.replay_frac > 0:
|
| 320 |
n_replay = int(n_sft * args.replay_frac / (1 - args.replay_frac))
|