Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import torch | |
| import torch.nn as nn | |
| import numpy as np | |
| from huggingface_hub import hf_hub_download | |
| import json | |
| # ========================================== | |
| # 1. إعدادات النظام والمتغيرات | |
| # ========================================== | |
| REPO_ID = "maherghanem86/Causal-Recommendation_Breaking_the_Filter_Bubble" | |
| NUM_USERS = 69797 | |
| NUM_ITEMS = 10681 | |
| device = torch.device('cpu') | |
| # ========================================== | |
| # 2. بناء معمارية النموذج | |
| # ========================================== | |
| class NeuMF(nn.Module): | |
| def __init__(self, num_users, num_items, mf_dim=32, mlp_layers=[64, 32, 16], dropout=0.2): | |
| super(NeuMF, self).__init__() | |
| self.embedding_user_mf = nn.Embedding(num_users, mf_dim) | |
| self.embedding_item_mf = nn.Embedding(num_items, mf_dim) | |
| self.embedding_user_mlp = nn.Embedding(num_users, mlp_layers[0]//2) | |
| self.embedding_item_mlp = nn.Embedding(num_items, mlp_layers[0]//2) | |
| self.mlp_model = nn.Sequential( | |
| nn.Linear(mlp_layers[0], mlp_layers[1]), | |
| nn.ReLU(), nn.Dropout(dropout), | |
| nn.Linear(mlp_layers[1], mlp_layers[2]), | |
| nn.ReLU(), nn.Dropout(dropout) | |
| ) | |
| self.prediction_layer = nn.Linear(mf_dim + mlp_layers[-1], 1) | |
| def predict_all(self, u_tensor, all_items_tensor): | |
| mf_vector = torch.mul(self.embedding_user_mf(u_tensor).expand(len(all_items_tensor), -1), self.embedding_item_mf(all_items_tensor)) | |
| mlp_vector = self.mlp_model(torch.cat([self.embedding_user_mlp(u_tensor).expand(len(all_items_tensor), -1), self.embedding_item_mlp(all_items_tensor)], dim=-1)) | |
| return self.prediction_layer(torch.cat([mf_vector, mlp_vector], dim=-1)).squeeze() | |
| # ========================================== | |
| # 3. تحميل النماذج والقواميس | |
| # ========================================== | |
| base_model = NeuMF(NUM_USERS, NUM_ITEMS).to(device) | |
| causal_model = NeuMF(NUM_USERS, NUM_ITEMS).to(device) | |
| movie_dict = {} | |
| user_bubbles_db = {} | |
| try: | |
| print("⏳ جاري تحميل النماذج والملفات من المستودع...") | |
| # تحميل الأوزان | |
| base_weights = hf_hub_download(repo_id=REPO_ID, filename="neumf_baseline.pth") | |
| causal_weights = hf_hub_download(repo_id=REPO_ID, filename="neumf_causal.pth") | |
| base_model.load_state_dict(torch.load(base_weights, map_location=device)) | |
| causal_model.load_state_dict(torch.load(causal_weights, map_location=device)) | |
| base_model.eval() | |
| causal_model.eval() | |
| # تحميل قواميس الترجمة التي قمتِ برفعها | |
| movies_path = hf_hub_download(repo_id=REPO_ID, filename="movie_names.json") | |
| with open(movies_path, "r", encoding="utf-8") as f: | |
| movie_dict = json.load(f) | |
| bubbles_path = hf_hub_download(repo_id=REPO_ID, filename="user_bubbles.json") | |
| with open(bubbles_path, "r", encoding="utf-8") as f: | |
| user_bubbles_db = json.load(f) | |
| print("✅ تم التحميل بنجاح!") | |
| except Exception as e: | |
| print(f"❌ حدث خطأ أثناء التحميل: {e}") | |
| # ========================================== | |
| # 4. دالة التنبؤ للواجهة | |
| # ========================================== | |
| def generate_recommendations(user_id): | |
| try: | |
| u_idx = int(user_id) | |
| if u_idx < 0 or u_idx >= NUM_USERS: | |
| return "رقم غير صالح.", "رقم غير صالح.", "رقم غير صالح." | |
| all_items_tensor = torch.arange(NUM_ITEMS).to(device) | |
| u_tensor = torch.tensor([u_idx]).to(device) | |
| # استخراج الفقاعة | |
| user_bubble = user_bubbles_db.get(str(u_idx), "غير متوفرة في العينة. يعتمد النظام على التفضيلات العامة.") | |
| formatted_bubble = f"🔍 الفقاعة المعلوماتية المكتشفة: {user_bubble}" | |
| # توقعات النظامين | |
| with torch.no_grad(): | |
| scores_base = base_model.predict_all(u_tensor, all_items_tensor).numpy() | |
| top_base = np.argsort(scores_base)[::-1][:10] | |
| scores_causal = causal_model.predict_all(u_tensor, all_items_tensor).numpy() | |
| top_causal = np.argsort(scores_causal)[::-1][:10] | |
| # ترجمة الأرقام إلى أسماء أفلام | |
| base_output = "\n\n".join([f"🎬 {movie_dict.get(str(item), f'فيلم غير معروف (ID: {item})')}" for item in top_base]) | |
| causal_output = "\n\n".join([f"✨ {movie_dict.get(str(item), f'فيلم غير معروف (ID: {item})')}" for item in top_causal]) | |
| return formatted_bubble, base_output, causal_output | |
| except ValueError: | |
| return "الرجاء إدخال رقم صحيح.", "خطأ", "خطأ" | |
| # ========================================== | |
| # 5. تصميم واجهة Gradio | |
| # ========================================== | |
| with gr.Blocks() as demo: | |
| gr.Markdown("# 🎓 تطبيق الاستدلال السببي لمعالجة التحيز في أنظمة التوصية") | |
| gr.Markdown("هذه الواجهة تقارن بين نظام التوصية التقليدي (الذي يعزز فقاعة التصفية) والنظام السببي المقترح (الذي يعزز استقلالية المستخدم).") | |
| with gr.Row(): | |
| user_input = gr.Textbox(label="أدخل رقم المستخدم (مثال: 0, 42, 60, 105)", placeholder="اكتب رقم المستخدم هنا...") | |
| btn = gr.Button("توليد التوصيات المقارنة 🚀", variant="primary") | |
| out_bubble = gr.Textbox(label="التشخيص المسبق لحالة المستخدم", lines=2) | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.Markdown("### ❌ النظام التقليدي (NeuMF Baseline)") | |
| gr.Markdown("*يميل إلى حصر المستخدم في تفضيلاته السابقة (فقاعة التصفية).*") | |
| out_base = gr.Textbox(label="التوصيات (العالم المرصود)", lines=15) | |
| with gr.Column(): | |
| gr.Markdown("### ✅ النظام السببي (Causal Autonomy NeuMF)") | |
| gr.Markdown("*يطبق التدخل السببي لكسر التنميط وتعزيز التنوع والاستقلالية.*") | |
| out_causal = gr.Textbox(label="التوصيات (العالم المضاد للواقع)", lines=15) | |
| btn.click(fn=generate_recommendations, inputs=user_input, outputs=[out_bubble, out_base, out_causal]) | |
| demo.launch(theme=gr.themes.Soft(), ssr_mode=False) |