keoka commited on
Commit
13dab33
·
1 Parent(s): 88c7d7c

إضافة جميع ملفات المشروع

Browse files
Dockerfile ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.9-slim
2
+
3
+ RUN useradd -m -u 1000 user
4
+ USER user
5
+ ENV PATH="/home/user/.local/bin:$PATH"
6
+ ENV HOME=/home/user \
7
+ HF_HOME=/home/user/.cache/huggingface
8
+
9
+ WORKDIR /app
10
+
11
+ COPY --chown=user requirements.txt .
12
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
13
+
14
+ COPY --chown=user . .
15
+
16
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
ai_threat_analyzer.py ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # =========================================
2
+ # GuardianX AI Threat Analyzer
3
+ # Version 2.0 - مع AraBERT (500MB)
4
+ # =========================================
5
+
6
+ import re
7
+ import pickle
8
+ import numpy as np
9
+ import pandas as pd
10
+ import os
11
+ import torch
12
+ from transformers import AutoTokenizer, AutoModel
13
+ from sklearn.linear_model import LogisticRegression
14
+
15
+
16
+ # ==============================
17
+ # إعدادات المشروع
18
+ # ==============================
19
+
20
+ DATA_FILE = "dataset.csv"
21
+ MODEL_FILE = "guardian_model.pkl"
22
+ ARABERT_PATH = "./arabic_model" # المسار المحلي للنموذج
23
+
24
+ LABELS = {
25
+ "safe": 0,
26
+ "scam": 1,
27
+ "threat": 2
28
+ }
29
+
30
+
31
+ # ==============================
32
+ # الكلاس الرئيسي
33
+ # ==============================
34
+
35
+ class ThreatAnalyzer:
36
+
37
+ # ---------------------------------
38
+ # INIT - مع AraBERT
39
+ # ---------------------------------
40
+ def __init__(self):
41
+
42
+ print("="*50)
43
+ print("🚀 جاري تحميل نموذج AraBERT...")
44
+ print("="*50)
45
+
46
+ # التحقق من وجود النموذج المحفوظ
47
+ if not os.path.exists(ARABERT_PATH):
48
+ print("⚠ لم أجد النموذج المحفوظ. سيتم تحميله من الإنترنت (قد يستغرق دقائق)")
49
+ # إذا لم يكن موجوداً، نحمله من Hugging Face
50
+ model_name = "aubmindlab/bert-base-arabertv2"
51
+ self.tokenizer = AutoTokenizer.from_pretrained(model_name)
52
+ self.bert_model = AutoModel.from_pretrained(model_name)
53
+ # نحفظه للمستقبل
54
+ os.makedirs(ARABERT_PATH, exist_ok=True)
55
+ self.tokenizer.save_pretrained(ARABERT_PATH)
56
+ self.bert_model.save_pretrained(ARABERT_PATH)
57
+ print("✅ تم تحميل وحفظ النموذج محلياً")
58
+ else:
59
+ # تحميل النموذج من المسار المحلي
60
+ print(f"📂 تحميل النموذج من: {ARABERT_PATH}")
61
+ self.tokenizer = AutoTokenizer.from_pretrained(ARABERT_PATH)
62
+ self.bert_model = AutoModel.from_pretrained(ARABERT_PATH)
63
+ print("✅ تم تحميل النموذج المحلي بنجاح!")
64
+
65
+ # تحديد الجهاز (GPU إن وجد)
66
+ self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
67
+ self.bert_model.to(self.device)
68
+ print(f"💻 الجهاز المستخدم: {self.device}")
69
+
70
+ # تحميل نموذج التصنيف (Logistic Regression)
71
+ self.classifier = LogisticRegression(max_iter=1000)
72
+
73
+ # محاولة تحميل النموذج المدرب مسبقاً
74
+ if os.path.exists(MODEL_FILE):
75
+ with open(MODEL_FILE, "rb") as f:
76
+ self.classifier = pickle.load(f)
77
+ print("✅ تم تحميل نموذج التصنيف من ملف")
78
+ else:
79
+ print("🔄 لا يوجد نموذج تصنيف محفوظ. سيتم تدريب نموذج جديد...")
80
+ self.train()
81
+
82
+
83
+ # ---------------------------------
84
+ # تحويل النص إلى متجه باستخدام AraBERT
85
+ # ---------------------------------
86
+ def text_to_vector(self, text):
87
+ """
88
+ تحويل النص إلى متجه (768 بعداً) باستخدام AraBERT
89
+ """
90
+ # تجهيز النص
91
+ inputs = self.tokenizer(
92
+ text,
93
+ return_tensors="pt",
94
+ padding=True,
95
+ truncation=True,
96
+ max_length=128
97
+ ).to(self.device)
98
+
99
+ # تمرير النص عبر النموذج
100
+ with torch.no_grad():
101
+ outputs = self.bert_model(**inputs)
102
+
103
+ # استخدام متوسط التشفيرات (Mean Pooling)
104
+ embedding = outputs.last_hidden_state.mean(dim=1).squeeze().cpu().numpy()
105
+
106
+ return embedding
107
+
108
+
109
+ # ---------------------------------
110
+ # تدريب النموذج
111
+ # ---------------------------------
112
+ def train(self):
113
+
114
+ print("📚 جاري قراءة بيانات التدريب...")
115
+ df = pd.read_csv(DATA_FILE)
116
+
117
+ X = []
118
+ y = []
119
+ skipped = 0
120
+
121
+ for idx, (text, label) in enumerate(zip(df["text"], df["label"])):
122
+ # عرض التقدم كل 100 جملة
123
+ if idx % 100 == 0:
124
+ print(f"⏳ معالجة الجملة {idx}/{len(df)}")
125
+
126
+ label = str(label).strip().lower()
127
+
128
+ if label not in LABELS:
129
+ skipped += 1
130
+ continue
131
+
132
+ # تحويل النص إلى متجه
133
+ vec = self.text_to_vector(text)
134
+ X.append(vec)
135
+ y.append(LABELS[label])
136
+
137
+ X = np.array(X)
138
+ y = np.array(y)
139
+
140
+ print(f"\n📊 إجمالي الجمل: {len(df)}")
141
+ print(f"📊 جمل مستخدمة في التدريب: {len(y)}")
142
+ print(f"⚠ جمل تم تخطيها: {skipped}")
143
+ print(f"���� شكل مصفوفة التدريب: {X.shape}")
144
+
145
+ # تدريب النموذج
146
+ print("\n🧠 جاري تدريب النموذج...")
147
+ self.classifier.fit(X, y)
148
+ print("✅ تم التدريب بنجاح!")
149
+
150
+ # حفظ النموذج
151
+ with open(MODEL_FILE, "wb") as f:
152
+ pickle.dump(self.classifier, f)
153
+ print("💾 تم حفظ النموذج في ملف")
154
+
155
+
156
+ # ---------------------------------
157
+ # التنبؤ
158
+ # ---------------------------------
159
+ def predict(self, text):
160
+
161
+ # تحويل النص إلى متجه
162
+ vec = self.text_to_vector(text).reshape(1, -1)
163
+
164
+ # التنبؤ
165
+ pred = self.classifier.predict(vec)[0]
166
+
167
+ # تحويل الرقم إلى تسمية
168
+ inv = {v: k for k, v in LABELS.items()}
169
+
170
+ return inv[pred]
171
+
172
+
173
+ # ---------------------------------
174
+ # إعادة التدريب (اختياري)
175
+ # ---------------------------------
176
+ def retrain(self, new_file=None):
177
+ """
178
+ إعادة تدريب النموذج على كل البيانات
179
+ new_file: (اختياري) ملف CSV إضافي
180
+ """
181
+ print("\n🔄 جاري إعادة التدريب...")
182
+
183
+ df = pd.read_csv(DATA_FILE)
184
+
185
+ if new_file:
186
+ df_new = pd.read_csv(new_file)
187
+ df = pd.concat([df, df_new], ignore_index=True)
188
+ df.to_csv(DATA_FILE, index=False)
189
+ print(f"📁 تم دمج ملف {new_file}")
190
+
191
+ X = []
192
+ y = []
193
+ skipped = 0
194
+
195
+ for text, label in zip(df["text"], df["label"]):
196
+ label = str(label).strip().lower()
197
+
198
+ if label not in LABELS:
199
+ skipped += 1
200
+ continue
201
+
202
+ vec = self.text_to_vector(text)
203
+ X.append(vec)
204
+ y.append(LABELS[label])
205
+
206
+ X = np.array(X)
207
+ y = np.array(y)
208
+
209
+ self.classifier.fit(X, y)
210
+
211
+ with open(MODEL_FILE, "wb") as f:
212
+ pickle.dump(self.classifier, f)
213
+
214
+ print(f"✅ تم إعادة التدريب!")
215
+ print(f"📊 الجمل المستخدمة: {len(y)}")
216
+
217
+
218
+ # ==============================
219
+ # اختبار مباشر
220
+ # ==============================
221
+
222
+ if __name__ == "__main__":
223
+
224
+ analyzer = ThreatAnalyzer()
225
+
226
+ tests = [
227
+ "مرحبا كيف حالك",
228
+ "ارسل المال والا بفضحك",
229
+ "فزت بجائزة اضغط الرابط",
230
+ "هات الرقم السري الآن",
231
+ "وينك يا صاحبي"
232
+ ]
233
+
234
+ print("\n🔍 اختبار التنبؤ:")
235
+ for t in tests:
236
+ result = analyzer.predict(t)
237
+ print(f" • {t} ➡ {result}")
app.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, Header, HTTPException, Request
2
+ from pydantic import BaseModel
3
+ from ai_threat_analyzer import ThreatAnalyzer
4
+ import os
5
+ import time
6
+ import logging
7
+
8
+ # محاولة استيراد Rate Limiter بشكل اختياري
9
+ try:
10
+ from fastapi_advanced_rate_limiter import SlidingWindowRateLimiter
11
+ RATE_LIMITER_AVAILABLE = True
12
+ except ImportError:
13
+ RATE_LIMITER_AVAILABLE = False
14
+ class SlidingWindowRateLimiter:
15
+ def __init__(self, *args, **kwargs): pass
16
+ def allow_request(self, client_id): return True
17
+ def get_wait_time(self, client_id): return 0
18
+
19
+ # إعداد التسجيل
20
+ logging.basicConfig(
21
+ level=logging.INFO,
22
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
23
+ handlers=[logging.FileHandler('api.log'), logging.StreamHandler()]
24
+ )
25
+ logger = logging.getLogger("guardianx")
26
+
27
+ app = FastAPI()
28
+
29
+ # قراءة مفتاح API من المتغيرات البيئية
30
+ API_KEY = os.getenv("API_KEY", "guardian123")
31
+
32
+ # إعداد Rate Limiter
33
+ limiter = SlidingWindowRateLimiter(capacity=10, fill_rate=10/60, scope="user", backend="memory")
34
+
35
+ analyzer = ThreatAnalyzer()
36
+
37
+ class TextRequest(BaseModel):
38
+ text: str
39
+
40
+ @app.get("/")
41
+ def home():
42
+ return {"message": "GuardianX API is running"}
43
+
44
+ @app.post("/predict")
45
+ async def predict(request: Request, data: TextRequest, x_api_key: str = Header(None)):
46
+ if not x_api_key:
47
+ logger.warning("طلب بدون مفتاح API")
48
+ raise HTTPException(status_code=401, detail="API Key مفقود")
49
+
50
+ if x_api_key != API_KEY:
51
+ logger.warning(f"محاولة بمفتاح غير صالح: {x_api_key[:5]}...")
52
+ raise HTTPException(status_code=403, detail="مفتاح غير صالح")
53
+
54
+ if RATE_LIMITER_AVAILABLE:
55
+ client_id = x_api_key
56
+ if not limiter.allow_request(client_id):
57
+ wait_time = limiter.get_wait_time(client_id)
58
+ logger.warning(f"كثرة طلبات من المستخدم: {x_api_key[:5]}...")
59
+ raise HTTPException(status_code=429, detail=f"عدد الطلبات كبير جداً. حاول بعد {wait_time:.0f} ثانية")
60
+
61
+ start_time = time.time()
62
+ try:
63
+ result = analyzer.predict(data.text)
64
+ processing_time = time.time() - start_time
65
+ logger.info(f"مستخدم: {x_api_key[:5]}... | نص: {data.text[:30]}... | نتيجة: {result} | وقت: {processing_time:.2f}ث")
66
+ return {"result": result}
67
+ except Exception as e:
68
+ logger.error(f"خطأ في التحليل: {str(e)}")
69
+ raise HTTPException(status_code=500, detail="خطأ داخلي في السيرفر")
arabic_model/config.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_cross_attention": false,
3
+ "architectures": [
4
+ "BertModel"
5
+ ],
6
+ "attention_probs_dropout_prob": 0.1,
7
+ "bos_token_id": null,
8
+ "classifier_dropout": null,
9
+ "dtype": "float32",
10
+ "eos_token_id": null,
11
+ "hidden_act": "gelu",
12
+ "hidden_dropout_prob": 0.1,
13
+ "hidden_size": 768,
14
+ "initializer_range": 0.02,
15
+ "intermediate_size": 3072,
16
+ "is_decoder": false,
17
+ "layer_norm_eps": 1e-12,
18
+ "max_position_embeddings": 512,
19
+ "model_type": "bert",
20
+ "num_attention_heads": 12,
21
+ "num_hidden_layers": 12,
22
+ "pad_token_id": 0,
23
+ "tie_word_embeddings": true,
24
+ "transformers_version": "5.3.0",
25
+ "type_vocab_size": 2,
26
+ "use_cache": true,
27
+ "vocab_size": 64000
28
+ }
arabic_model/model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c0cc902dbaa87d7cf826caa2b97bf4fc3749434258e0094fb60b256088238ad3
3
+ size 540795728
arabic_model/tokenizer.json ADDED
The diff for this file is too large to render. See raw diff
 
arabic_model/tokenizer_config.json ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "backend": "tokenizers",
3
+ "cls_token": "[CLS]",
4
+ "do_basic_tokenize": true,
5
+ "do_lower_case": false,
6
+ "is_local": false,
7
+ "mask_token": "[MASK]",
8
+ "max_len": 512,
9
+ "model_max_length": 512,
10
+ "never_split": [
11
+ "+ك",
12
+ "+كما",
13
+ "ك+",
14
+ "+وا",
15
+ "+ين",
16
+ "و+",
17
+ "+كن",
18
+ "+ان",
19
+ "+هم",
20
+ "+ة",
21
+ "[بريد]",
22
+ "لل+",
23
+ "+ي",
24
+ "+ت",
25
+ "+ن",
26
+ "س+",
27
+ "ل+",
28
+ "[مستخدم]",
29
+ "+كم",
30
+ "+ا",
31
+ "ب+",
32
+ "ف+",
33
+ "+نا",
34
+ "+ها",
35
+ "+ون",
36
+ "+هما",
37
+ "ال+",
38
+ "+ه",
39
+ "+هن",
40
+ "+ات",
41
+ "[رابط]"
42
+ ],
43
+ "pad_token": "[PAD]",
44
+ "sep_token": "[SEP]",
45
+ "strip_accents": null,
46
+ "tokenize_chinese_chars": true,
47
+ "tokenizer_class": "BertTokenizer",
48
+ "unk_token": "[UNK]"
49
+ }
guardian_model.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0931346d7e109f9e38356e2f5e86fdc7455aaa68d56912119dd815433a8f22f5
3
+ size 19177
requirements.txt ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn[standard]
3
+ gensim
4
+ numpy<2.0.0
5
+ scikit-learn
6
+ joblib
7
+ python-multipart
8
+ requests
9
+ pandas
10
+ redis
11
+ gdown