JEV-CPU / semif_cpu.py
Meanblock's picture
Add JEV-CPU: CPU port of SemIf + web UI
7845694 verified
Raw History Blame Contribute Delete
4.74 kB
#!/usr/bin/env python3
"""
SemIf ๋ฅผ ์ด Linux CPU PC์—์„œ ๋กœ์ปฌ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•œ ์–‡์€ shim.
SemIf( github.com/TheoLeeCJ/SemIf )์˜ ์œ ์ผํ•œ GPU ๊ฐ•์ œ ์ง€์ ์€
core.load_causal_model() ๋ฟ์ด๋‹ค. ์Šค์ฝ”์–ด๋ง ๋กœ์ง(direct.score / shared.score_shared)์€
device = next(model.parameters()).device ๋ฅผ ๋”ฐ๋ผ๊ฐ€๋ฏ€๋กœ CPU์—์„œ ๊ทธ๋Œ€๋กœ ๋™์ž‘ํ•œ๋‹ค.
์—ฌ๊ธฐ์„œ๋Š” CUDA ๋กœ๋” ๋Œ€์‹  CPU(float32) ๋กœ๋”๋ฅผ ์จ์„œ ๋ชจ๋ธ์„ ์˜ฌ๋ฆฌ๊ณ ,
SemIf ์˜ ์‹ค์ œ direct.score() ๋ฅผ ํ˜ธ์ถœํ•ด '์˜ต์…˜ ํ™•๋ฅ (semantic if)'์„ ์ฝ๋Š”๋‹ค.
์ „์ œ:
- SemIf ์ €์žฅ์†Œ๊ฐ€ /tmp/SemIf ์— clone ๋˜์–ด ์žˆ์Œ (SEMIF_DIR ๋กœ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ)
- pip install torch transformers accelerate (CPU)
์‚ฌ์šฉ:
python semif_cpu.py
"""
import os
import sys
import time
# SemIf ์†Œ์Šค ์œ„์น˜ ํƒ์ง€: 1) SEMIF_DIR ํ™˜๊ฒฝ๋ณ€์ˆ˜, 2) ์ด ํŒŒ์ผ๊ณผ ๊ฐ™์€ ๋ ˆํฌ์˜ ./src,
# 3) /tmp/SemIf (๊ฐœ๋ฐœ์šฉ clone). ์ฒ˜์Œ ๋ฐœ๊ฒฌ๋˜๋Š” ๊ณณ์„ ์‚ฌ์šฉํ•œ๋‹ค.
_HERE = os.path.dirname(os.path.abspath(__file__))
_CANDIDATES = [
os.environ.get("SEMIF_DIR"),
_HERE, # ๋ ˆํฌ ๋ฃจํŠธ์— src/semif_phase1 ์ด ์žˆ๋Š” ๊ฒฝ์šฐ (JEV-CPU)
"/tmp/SemIf", # ๊ฐœ๋ฐœ์šฉ clone
]
for _base in _CANDIDATES:
if _base and os.path.isdir(os.path.join(_base, "src", "semif_phase1")):
SEMIF_DIR = _base
break
else:
raise RuntimeError(
"SemIf ์†Œ์Šค๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. SEMIF_DIR ํ™˜๊ฒฝ๋ณ€์ˆ˜๋กœ ๊ฒฝ๋กœ๋ฅผ ์ง€์ •ํ•˜์„ธ์š” "
"(src/semif_phase1 ๋ฅผ ํฌํ•จํ•ด์•ผ ํ•จ)."
)
sys.path.insert(0, os.path.join(SEMIF_DIR, "src"))
import torch
import transformers
from semif_phase1.direct import score as direct_score
# openjev.com/SemIf ๊ฐ€ ์“ฐ๋Š” ๊ฐ€์žฅ ์ž‘์€ ๋ชจ๋ธ. ์›๊ฒฉ ๋กœ๋“œ๋Š” 40์ž ์ปค๋ฐ‹ revision ์„ ์š”๊ตฌํ•œ๋‹ค.
MODEL = "Qwen/Qwen3-0.6B"
REVISION = os.environ.get("QWEN_REV", "main") # ํ•„์š”์‹œ 40์ž ์ปค๋ฐ‹ ํ•ด์‹œ๋กœ ๊ณ ์ •
def load_causal_model_cpu(source: str, revision: str):
"""core.load_causal_model ์˜ CPU ๋ฒ„์ „ (CUDA ๊ฒ€์‚ฌ/ device_map ์ œ๊ฑฐ)."""
common = {"trust_remote_code": False}
if revision and revision != "main":
common["revision"] = revision
config = transformers.AutoConfig.from_pretrained(source, **common)
tokenizer = transformers.AutoTokenizer.from_pretrained(source, **common)
model = transformers.AutoModelForCausalLM.from_pretrained(
source,
config=config,
dtype=torch.float32, # CPU ์•ˆ์ •์„ฑ ์šฐ์„ 
low_cpu_mem_usage=True,
**common,
)
model.eval()
metadata = {
"source": source,
"revision": revision,
"dtype": "float32",
"device": "cpu",
"torch_version": torch.__version__,
"transformers_version": transformers.__version__,
}
return model, tokenizer, metadata
def main():
print(f"[load] {MODEL} @ {REVISION} (CPU / float32)")
t0 = time.time()
model, tokenizer, meta = load_causal_model_cpu(MODEL, REVISION)
print(f"[load] done in {time.time()-t0:.1f}s "
f"(params={sum(p.numel() for p in model.parameters())/1e9:.2f}B, "
f"transformers={transformers.__version__})")
# SemIf ์˜ ๊ฒฐ์ •(row) ์Šคํ‚ค๋งˆ: state(์ฆ๊ฑฐ) + question(๊ธฐ์ค€) + options(2~16๊ฐœ)
rows = [
{
"id": "sentiment",
"state": "๋ฐฐ์†ก์ด 3์ผ์ด๋‚˜ ๋Šฆ์—ˆ๊ณ  ๊ณ ๊ฐ์„ผํ„ฐ๋Š” ์—ฐ๊ฒฐ๋„ ์•ˆ ๋์–ด์š”. ์ •๋ง ์‹ค๋ง์ž…๋‹ˆ๋‹ค.",
"question": "Classify the customer's sentiment.",
"options": [
{"id": "positive", "description": "Positive / satisfied"},
{"id": "neutral", "description": "Neutral"},
{"id": "negative", "description": "Negative / dissatisfied"},
],
},
{
"id": "route",
"state": "I was double charged and need a refund before Friday.",
"question": "Which team should handle this ticket?",
"options": [
{"id": "billing", "description": "Billing / payments"},
{"id": "tech", "description": "Technical support"},
{"id": "sales", "description": "Sales"},
],
},
]
for row in rows:
print(f"\n=== decision: {row['id']} ===")
r = direct_score(model, tokenizer, row, meta)
pairs = sorted(zip(r["option_ids"], r["probabilities"]),
key=lambda x: -x[1])
winner = pairs[0][0]
print(f" โ†’ ์„ ํƒ: {winner}")
for oid, p in pairs:
bar = "โ–ˆ" * int(p * 30)
print(f" {oid:10s} {p*100:5.1f}% {bar}")
print(f" (forward {r['forward_seconds']:.1f}s, {r['input_tokens']} tok, "
f"readout: {r['readout']})")
if __name__ == "__main__":
main()