AhmedBenElsayedBenAhmed's picture
Merge branch 'اسم-الفرع-الجديد' into main
faa6d61
Raw History Blame
10.8 kB
# ===============================
# 1️⃣ استيراد المكتبات
# ===============================
import os
os.environ["HF_HOME"] = "/tmp"
os.environ["TRANSFORMERS_CACHE"] = "/tmp"
os.environ["PEFT_HOME"] = "/tmp"
from transformers import AutoModelForCausalLM, AutoTokenizer, Trainer, TrainingArguments
from peft import LoraConfig, get_peft_model, TaskType, PeftModel
from flask import Flask, request, jsonify
from flask_cors import CORS
import threading, torch, json
from datasets import Dataset
# ===============================
# 2️⃣ إعداد المتغيرات
# ===============================
MODEL_NAME = "Qwen/Qwen2.5-0.5B-Instruct"
FLASK_PORT = 7860
SAVE_DIR = "/tmp/lora_model"
DATA_DIR = "/tmp/data"
os.makedirs(DATA_DIR, exist_ok=True)
os.makedirs(SAVE_DIR, exist_ok=True)
DATASET_FILE = os.path.join(DATA_DIR, "dataset.json")
# ===============================
# 3️⃣ تحديد الجهاز
# ===============================
device = "cuda" if torch.cuda.is_available() else "cpu"
print("🔹 Using device:", device)
# ===============================
# 4️⃣ تحميل الموديل الأساسي بأمان
# ===============================
print("🔄 Loading base model...")
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
try:
model = AutoModelForCausalLM.from_pretrained(
MODEL_NAME,
device_map="auto" if device=="cuda" else {"": "cpu"},
torch_dtype=torch.float16 if device=="cuda" else torch.float32,
load_in_8bit=(device=="cuda"),
trust_remote_code=True
)
model.to(device)
print("✅ Base model loaded!")
except Exception as e:
print("⚠️ Failed to load base model:", e)
raise e
# ===============================
# 5️⃣ إعداد LoRA
# ===============================
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=2,
lora_alpha=8,
lora_dropout=0.05,
target_modules=["q_proj", "v_proj"]
)
model = get_peft_model(model, lora_config)
model.to(device)
print("✅ LoRA model ready on device:", device)
# ===============================
# 6️⃣ Auto load LoRA weights
# ===============================
if os.path.exists(SAVE_DIR):
try:
base_model = AutoModelForCausalLM.from_pretrained(
MODEL_NAME,
device_map="auto" if device=="cuda" else {"": "cpu"},
torch_dtype=torch.float16 if device=="cuda" else torch.float32,
load_in_8bit=(device=="cuda"),
trust_remote_code=True
)
model = PeftModel.from_pretrained(base_model, SAVE_DIR)
model.to(device)
print("✅ LoRA weights auto-loaded from disk!")
except Exception as e:
print("⚠️ Failed to auto-load LoRA weights:", e)
else:
print("ℹ️ No saved LoRA weights found, running with base model.")
# ===============================
# 7️⃣ إعداد Flask API
# ===============================
app = Flask(__name__)
CORS(app)
trainer = None
current_dataset = None
# Fake DB
users_db = {
"1": {"name": "Ahmed Elsayed", "role": "Student"},
"2": {"name": "Mona Ali", "role": "Lawyer"},
"3": {"name": "Omar Hassan", "role": "Client"}
}
# ===============================
# 7a️⃣ توليد نصوص
# ===============================
@app.route("/generate", methods=["POST"])
def generate():
try:
data = request.json
prompt = data.get("prompt", "").strip()
user_id = str(data.get("userId", "")).strip()
user_info = users_db.get(user_id)
if not user_info:
return jsonify({"error": "User not found"}), 404
user_name = user_info["name"]
if current_dataset is None:
return jsonify({
"messages": [
{"role": "assistant", "content": f"⚖️ أهلاً {user_name}! أنا زين، مساعدك الذكي. برجاء طرح سؤال قانوني."}
]
})
# Search in dataset
for item in current_dataset:
lines = item["text"].split("\n")
for line in lines:
if line.lower().startswith("user:") and prompt in line:
response_messages = []
for l in lines:
if ":" in l:
role, content = l.split(":", 1)
response_messages.append({
"role": role.strip().lower(),
"content": content.strip()
})
response_messages.insert(0, {"role": "assistant", "content": f"أهلاً {user_name} 👋"})
return jsonify({"messages": response_messages})
# Fallback: generate
inputs = tokenizer(prompt, return_tensors="pt").to(device)
outputs = model.generate(
**inputs,
max_new_tokens=150,
temperature=0.7,
do_sample=True
)
reply = tokenizer.decode(outputs[0], skip_special_tokens=True)
return jsonify({
"messages": [
{"role": "assistant", "content": f"أهلاً {user_name} 👋"},
{"role": "assistant", "content": reply}
]
})
except Exception as e:
return jsonify({"messages": [{"role": "assistant", "content": f"خطأ: {str(e)}"}]})
# ===============================
# 7b️⃣ تحميل الداتا
# ===============================
def load_dataset():
global current_dataset
if os.path.exists(DATASET_FILE):
with open(DATASET_FILE, "r", encoding="utf-8") as f:
try:
data = json.load(f)
except:
data = []
if data:
formatted = []
for item in data:
if "messages" in item:
conversation = ""
for msg in item["messages"]:
conversation += f"{msg['role'].capitalize()}: {msg['content']}\n"
formatted.append({"text": conversation.strip()})
elif "text" in item:
formatted.append({"text": item["text"]})
current_dataset = Dataset.from_list(formatted)
print(f"✅ Loaded {len(formatted)} records from dataset.json")
else:
current_dataset = None
else:
current_dataset = None
load_dataset()
# ===============================
# 7c️⃣ تدريب LoRA (Async)
# ===============================
@app.route("/train", methods=["POST"])
def train():
global trainer, current_dataset, model
try:
data = []
epochs = 1
batch_size = 1
# 1️⃣ JSON مباشر
if request.is_json:
body = request.get_json()
if isinstance(body, list):
data = body
elif isinstance(body, dict) and "data" in body:
data = body["data"]
else:
return jsonify({"error": "JSON format not recognized"}), 400
epochs = int(body.get("epochs", 1)) if isinstance(body, dict) else 1
batch_size = int(body.get("batch_size", 1)) if isinstance(body, dict) else 1
# 2️⃣ form-data ملف
else:
file = request.files.get("file")
if file:
data = json.load(file)
epochs = int(request.form.get("epochs", 1))
batch_size = int(request.form.get("batch_size", 1))
else:
return jsonify({"error": "No file uploaded or JSON provided"}), 400
# دمج وإزالة التكرارات حسب محتوى الرسائل
old_data = []
if os.path.exists(DATASET_FILE):
with open(DATASET_FILE, "r", encoding="utf-8") as f:
try:
old_data = json.load(f)
except:
old_data = []
combined = old_data + data
unique_data, seen_texts = [], set()
for item in combined:
if "messages" in item:
text_key = "\n".join([f"{m['role'].strip().lower()}:{m['content'].strip()}" for m in item["messages"]])
elif "text" in item:
text_key = item["text"].strip()
else:
continue
if text_key not in seen_texts:
seen_texts.add(text_key)
unique_data.append(item)
# الحفظ
with open(DATASET_FILE, "w", encoding="utf-8") as f:
json.dump(unique_data, f, ensure_ascii=False, indent=2)
# تجهيز Dataset للتدريب
formatted = []
for item in unique_data:
if "messages" in item:
conversation = ""
for msg in item["messages"]:
conversation += f"{msg['role'].capitalize()}: {msg['content']}\n"
formatted.append({"text": conversation.strip()})
elif "text" in item:
formatted.append({"text": item["text"]})
current_dataset = Dataset.from_list(formatted)
def tokenize_function(examples):
return tokenizer(examples["text"], truncation=True, padding="max_length", max_length=256)
tokenized_dataset = current_dataset.map(tokenize_function, batched=True, remove_columns=["text"])
training_args = TrainingArguments(
output_dir=SAVE_DIR,
per_device_train_batch_size=batch_size,
num_train_epochs=epochs,
logging_steps=2,
save_strategy="epoch",
fp16=torch.cuda.is_available(),
report_to=None
)
trainer = Trainer(model=model, args=training_args, train_dataset=tokenized_dataset)
# تشغيل التدريب في Thread منفصل
def run_training():
print("🔄 Training started...")
trainer.train()
trainer.save_model(SAVE_DIR)
print("✅ Training completed & model saved!")
threading.Thread(target=run_training, daemon=True).start()
return jsonify({
"status": "training started",
"epochs": epochs,
"batch_size": batch_size,
"records": len(unique_data)
})
except Exception as e:
return jsonify({"error": str(e)})
# ===============================
# 8️⃣ تشغيل Flask
# ===============================
@app.route("/", methods=["GET"])
def home():
return jsonify({
"status": "ok",
"message": "Graduation Project API is running 🚀",
"endpoints": ["/generate", "/train"]
})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=FLASK_PORT, threaded=True)