AhmedBenElsayedBenAhmed commited on
Commit
4c4fd55
·
1 Parent(s): 65bf830

add project

Browse files
Files changed (3) hide show
  1. Dockerfile +30 -0
  2. app.py +238 -0
  3. requirements.txt +6 -0
Dockerfile ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ================================
2
+ # 1️⃣ استخدم Python كـ base image
3
+ # ================================
4
+ FROM python:3.10-slim
5
+
6
+ # ================================
7
+ # 2️⃣ ضبط مكان العمل
8
+ # ================================
9
+ WORKDIR /app
10
+
11
+ # ================================
12
+ # 3️⃣ نسخ الملفات
13
+ # ================================
14
+ COPY requirements.txt requirements.txt
15
+ COPY . .
16
+
17
+ # ================================
18
+ # 4️⃣ تثبيت المكتبات
19
+ # ================================
20
+ RUN pip install --no-cache-dir -r requirements.txt
21
+
22
+ # ================================
23
+ # 5️⃣ تحديد المنفذ (HF Spaces يشتغل على 7860 أو 8080)
24
+ # ================================
25
+ EXPOSE 7860
26
+
27
+ # ================================
28
+ # 6️⃣ تشغيل السيرفر
29
+ # ================================
30
+ CMD ["python", "app.py"]
app.py ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ===============================
2
+ # 1️⃣ استيراد المكتبات
3
+ # ===============================
4
+ from transformers import AutoModelForCausalLM, AutoTokenizer, Trainer, TrainingArguments
5
+ from peft import LoraConfig, get_peft_model, TaskType, PeftModel
6
+ from flask import Flask, request, jsonify
7
+ from flask_cors import CORS
8
+ import threading, torch
9
+ from datasets import Dataset
10
+ import os, json
11
+
12
+ # ===============================
13
+ # 2️⃣ إعداد المتغيرات
14
+ # ===============================
15
+ MODEL_NAME = "Qwen/Qwen2.5-0.5B-Instruct"
16
+ SAVE_DIR = "./lora_model"
17
+ DATASET_FILE = "dataset.json"
18
+
19
+ # ===============================
20
+ # 3️⃣ تحميل الموديل الأساسي
21
+ # ===============================
22
+ print("🔄 Loading base model...")
23
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
24
+ model = AutoModelForCausalLM.from_pretrained(
25
+ MODEL_NAME,
26
+ device_map="auto",
27
+ torch_dtype=torch.float16
28
+ )
29
+ print("✅ Base model loaded!")
30
+
31
+ # ===============================
32
+ # 4️⃣ إعداد LoRA
33
+ # ===============================
34
+ lora_config = LoraConfig(
35
+ task_type=TaskType.CAUSAL_LM,
36
+ r=2,
37
+ lora_alpha=8,
38
+ lora_dropout=0.05,
39
+ target_modules=["q_proj", "v_proj"]
40
+ )
41
+ model = get_peft_model(model, lora_config)
42
+ print("✅ LoRA model ready!")
43
+
44
+ # ===============================
45
+ # 🟢 Auto load LoRA weights on startup
46
+ # ===============================
47
+ if os.path.exists(SAVE_DIR):
48
+ try:
49
+ model = AutoModelForCausalLM.from_pretrained(
50
+ MODEL_NAME,
51
+ device_map="auto",
52
+ torch_dtype=torch.float16
53
+ )
54
+ model = PeftModel.from_pretrained(model, SAVE_DIR)
55
+ print("✅ LoRA weights auto-loaded from disk! [Fine-tuned model running]")
56
+ except Exception as e:
57
+ print("⚠️ Failed to auto-load LoRA weights:", str(e))
58
+ else:
59
+ print("ℹ️ No saved LoRA weights found, running with base model.")
60
+
61
+ # ===============================
62
+ # 5️⃣ إعداد Flask API
63
+ # ===============================
64
+ app = Flask(__name__)
65
+ CORS(app)
66
+
67
+ trainer = None
68
+ current_dataset = None
69
+
70
+ # ===============================
71
+ # 🟢 تحميل الداتا من الملف عند البداية
72
+ # ===============================
73
+ def load_dataset():
74
+ global current_dataset
75
+ if os.path.exists(DATASET_FILE):
76
+ with open(DATASET_FILE, "r", encoding="utf-8") as f:
77
+ try:
78
+ data = json.load(f)
79
+ except:
80
+ data = []
81
+
82
+ if data:
83
+ formatted = []
84
+ for item in data:
85
+ if "messages" in item:
86
+ conversation = ""
87
+ for msg in item["messages"]:
88
+ conversation += f"{msg['role'].capitalize()}: {msg['content']}\n"
89
+ formatted.append({"text": conversation.strip()})
90
+ elif "text" in item:
91
+ formatted.append({"text": item["text"]})
92
+ current_dataset = Dataset.from_list(formatted)
93
+ print(f"✅ Loaded {len(formatted)} records from dataset.json")
94
+ else:
95
+ current_dataset = None
96
+ else:
97
+ current_dataset = None
98
+
99
+ load_dataset()
100
+
101
+ # ===============================
102
+ # 5a️⃣ توليد نصوص (generate)
103
+ # ===============================
104
+ @app.route("/generate", methods=["POST"])
105
+ def generate():
106
+ try:
107
+ data = request.json
108
+ prompt = data.get("prompt", "").strip()
109
+
110
+ if current_dataset is None:
111
+ return jsonify({
112
+ "messages": [
113
+ {"role": "assistant", "content": "⚖️ أنا زين، مساعد ذكي للاستشارات القانونية. برجاء طرح سؤال قانوني."}
114
+ ]
115
+ })
116
+
117
+ # البحث في الداتا
118
+ for item in current_dataset:
119
+ lines = item["text"].split("\n")
120
+ for line in lines:
121
+ if line.lower().startswith("user:") and prompt in line:
122
+ response_messages = []
123
+ for l in lines:
124
+ if ":" in l:
125
+ role, content = l.split(":", 1)
126
+ response_messages.append({
127
+ "role": role.strip().lower(),
128
+ "content": content.strip()
129
+ })
130
+ return jsonify({"messages": response_messages})
131
+
132
+ # 🟡 لو مفيش تطابق
133
+ return jsonify({
134
+ "messages": [
135
+ {"role": "assistant", "content": "⚖️ هذا السؤال ليس من ضمن اختصاصي. أنا زين مساعد قانوني مخصص للإجابة على الاستشارات القانونية فقط."}
136
+ ]
137
+ })
138
+
139
+ except Exception as e:
140
+ return jsonify({"messages": [{"role": "assistant", "content": f"خطأ: {str(e)}"}]})
141
+
142
+ # ===============================
143
+ # 5b️⃣ تدريب LoRA (Async) + حفظ
144
+ # ===============================
145
+ @app.route("/train", methods=["POST"])
146
+ def train():
147
+ global trainer, current_dataset, model
148
+ try:
149
+ data = []
150
+ epochs = 1
151
+ batch_size = 1
152
+
153
+ # 📌 1) JSON request
154
+ if request.is_json:
155
+ body = request.get_json()
156
+ data = body.get("data", [])
157
+ epochs = int(body.get("epochs", 1))
158
+ batch_size = int(body.get("batch_size", 1))
159
+
160
+ # 📌 2) Form-data request
161
+ else:
162
+ epochs = int(request.form.get("epochs", 1))
163
+ batch_size = int(request.form.get("batch_size", 1))
164
+ file = request.files.get("file")
165
+ if file:
166
+ data = json.load(file)
167
+ else:
168
+ return jsonify({"error": "No file uploaded"}), 400
169
+
170
+ # دمج وإزالة التكرارات
171
+ old_data = []
172
+ if os.path.exists(DATASET_FILE):
173
+ with open(DATASET_FILE, "r", encoding="utf-8") as f:
174
+ try:
175
+ old_data = json.load(f)
176
+ except:
177
+ old_data = []
178
+
179
+ combined = old_data + data
180
+ unique_data, seen = [], set()
181
+ for item in combined:
182
+ key = json.dumps(item, ensure_ascii=False)
183
+ if key not in seen:
184
+ seen.add(key)
185
+ unique_data.append(item)
186
+
187
+ # الحفظ
188
+ with open(DATASET_FILE, "w", encoding="utf-8") as f:
189
+ json.dump(unique_data, f, ensure_ascii=False, indent=2)
190
+
191
+ # تجهيز الداتا
192
+ formatted = []
193
+ for item in unique_data:
194
+ if "messages" in item:
195
+ conversation = ""
196
+ for msg in item["messages"]:
197
+ conversation += f"{msg['role'].capitalize()}: {msg['content']}\n"
198
+ formatted.append({"text": conversation.strip()})
199
+ elif "text" in item:
200
+ formatted.append({"text": item["text"]})
201
+
202
+ current_dataset = Dataset.from_list(formatted)
203
+
204
+ def tokenize_function(examples):
205
+ return tokenizer(examples["text"], truncation=True, padding="max_length", max_length=256)
206
+
207
+ tokenized_dataset = current_dataset.map(tokenize_function, batched=True, remove_columns=["text"])
208
+
209
+ training_args = TrainingArguments(
210
+ output_dir=SAVE_DIR,
211
+ per_device_train_batch_size=batch_size,
212
+ num_train_epochs=epochs,
213
+ logging_steps=2,
214
+ save_strategy="epoch",
215
+ fp16=True,
216
+ report_to=None
217
+ )
218
+
219
+ trainer = Trainer(model=model, args=training_args, train_dataset=tokenized_dataset)
220
+
221
+ def run_training():
222
+ print("🔄 Training started...")
223
+ trainer.train()
224
+ trainer.save_model(SAVE_DIR)
225
+ print("✅ Training completed & model saved!")
226
+
227
+ threading.Thread(target=run_training, daemon=True).start()
228
+
229
+ return jsonify({"status": "training started", "epochs": epochs, "batch_size": batch_size, "records": len(unique_data)})
230
+
231
+ except Exception as e:
232
+ return jsonify({"error": str(e)})
233
+
234
+ # ===============================
235
+ # 6️⃣ تشغيل Flask
236
+ # ===============================
237
+ if __name__ == "__main__":
238
+ app.run(host="0.0.0.0", port=7860, threaded=True)
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ flask
2
+ flask-cors
3
+ transformers
4
+ torch
5
+ peft
6
+ datasets