leilaghomashchi commited on
Commit
5ef727d
·
verified ·
1 Parent(s): f224893

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +95 -112
app.py CHANGED
@@ -4,58 +4,63 @@ from typing import Dict, Any
4
  import os
5
  from dataclasses import dataclass
6
  import re
7
- from llama_cpp import Llama
8
- from huggingface_hub import hf_hub_download
9
 
10
  @dataclass
11
  class LocalModelConfig:
12
- """تنظیمات مدل محلی GGUF - Qwen2.5-32B"""
13
- repo_id: str = "Qwen/Qwen2.5-32B-Instruct-GGUF"
14
- filename: str = "qwen2.5-32b-instruct-q4_k_m.gguf"
15
- max_tokens: int = 8000
16
  temperature: float = 0.3
17
  top_p: float = 0.8
18
- n_ctx: int = 4096
19
- n_threads: int = 4 # کمتر برای Spaces
20
- n_gpu_layers: int = 50
21
 
22
- class LocalCerebrasAnonymizer:
23
- """سیستم ناشناس‌سازی متون مالی فارسی با مدل محلی"""
24
 
25
  def __init__(self):
26
  self.config = LocalModelConfig()
27
- self.llm = None
 
28
  self.model_loaded = False
29
 
30
  def load_model(self) -> str:
31
  """بارگذاری مدل از HuggingFace"""
32
  try:
33
- print(f"🤖 درحال دانلود مدل از HuggingFace...")
34
- print(f"📦 Repo: {self.config.repo_id}")
35
- print(f"📄 Filename: {self.config.filename}")
36
 
37
- # دانلود مدل
38
- model_path = hf_hub_download(
39
- repo_id=self.config.repo_id,
40
- filename=self.config.filename,
41
- local_dir="./models",
42
- local_dir_use_symlinks=False
43
- )
44
 
45
- print(f"✅ مدل دانلود شد: {model_path}")
46
- print(f"🤖 درحال بارگذاری مدل...")
 
47
 
48
- self.llm = Llama(
49
- model_path=model_path,
50
- n_ctx=self.config.n_ctx,
51
- n_threads=self.config.n_threads,
52
- n_gpu_layers=self.config.n_gpu_layers,
53
- verbose=False
54
- )
 
 
 
 
 
 
 
 
 
 
55
 
 
56
  self.model_loaded = True
 
57
  print("✅ مدل با موفقیت بارگذاری شد\n")
58
- return "✅ مدل آماده است"
59
 
60
  except Exception as e:
61
  error_msg = f"❌ خطا: {str(e)}"
@@ -69,21 +74,20 @@ class LocalCerebrasAnonymizer:
69
  ⚠️ CRITICAL: در پاسخ نهایی خود، فقط و فقط متن ناشناس‌سازی شده را برگردانید، بدون هیچ توضیح، تحلیل، یا تگ اضافی.
70
 
71
  ## قوانین اندیس‌گذاری:
72
- 1. **ترتیب پیوسته**: company-01, company-02, ... | person-01, person-02, ... | amount-01, amount-02, ... | percent-01, percent-02, ...
73
- 2. **ثبات**: اگر "همراه اول" → company-01 شد، در تمام متن همان باشد
74
- 3. **نام مستعار**: "فاما" = "فولاد مبارکه" → هر دو company-01
75
 
76
  ## انواع موجودیت:
77
- - **company-XX**: شرکت‌ها، بانک‌ها، سازمان‌ها
78
- - **person-XX**: نام و نام خانوادگی اشخاص
79
- - **amount-XX**: مبالغ - واحد را حفظ کن
80
- - **percent-XX**: درصدها
81
 
82
  ## مثال:
83
  ورودی: ایران خودرو در اسفند 1402 حدود 23 هزار میلیارد درآمد کسب کرد که 4.58 درصد افزایش داشت.
84
  خروجی: company-01 در اسفند 1402 حدود amount-01 درآمد کسب کرد که percent-01 افزایش داشت.
85
 
86
- ⚠️ یادآوری: فقط متن ناشناس‌شده."""
87
 
88
  def anonymize_text(self, text: str) -> Dict[str, Any]:
89
  """ناشناس‌سازی متن"""
@@ -94,28 +98,49 @@ class LocalCerebrasAnonymizer:
94
  return {"success": False, "error": "متن ورودی خالی است"}
95
 
96
  try:
 
 
 
 
 
 
 
97
  messages = [
98
- {"role": "system", "content": self._create_system_prompt()},
99
- {"role": "user", "content": text}
100
  ]
101
 
102
- prompt = self._format_prompt(messages)
 
 
 
 
 
 
 
 
103
 
104
- print(f"⏳ پردازش متن... (طول: {len(text)} کاراکتر)")
 
 
 
 
 
 
 
 
 
105
 
106
- response = self.llm(
107
- prompt,
108
- max_tokens=self.config.max_tokens,
109
- temperature=self.config.temperature,
110
- top_p=self.config.top_p,
111
- stop=["</s>", "[/INST]", "### User:"]
112
- )
113
 
114
- content = response["choices"][0]["text"].strip()
 
 
 
 
115
 
116
  # پاک‌سازی
117
- content = self._remove_thinking_tags(content)
118
- content = self._clean_markdown(content)
119
  content = self._clean_explanations(content)
120
  content = content.strip()
121
 
@@ -133,40 +158,8 @@ class LocalCerebrasAnonymizer:
133
  except Exception as e:
134
  return {"success": False, "error": f"خطا: {str(e)}"}
135
 
136
- def _format_prompt(self, messages: list) -> str:
137
- """فرمت prompt برای Qwen2.5"""
138
- formatted = ""
139
- for message in messages:
140
- role = message["role"]
141
- content = message["content"]
142
- if role == "system":
143
- formatted += f"{content}\n\n"
144
- elif role == "user":
145
- formatted += f"[INST] {content} [/INST]\n"
146
- elif role == "assistant":
147
- formatted += f"{content}\n\n"
148
- return formatted
149
-
150
- def _remove_thinking_tags(self, content: str) -> str:
151
- content = re.sub(r'<think>.*?</think>', '', content, flags=re.DOTALL)
152
- content = re.sub(r'</?think>', '', content)
153
- return content.strip()
154
-
155
- def _clean_markdown(self, content: str) -> str:
156
- if "```" in content:
157
- lines = content.split('\n')
158
- clean_lines = []
159
- skip = False
160
- for line in lines:
161
- if line.strip().startswith('```'):
162
- skip = not skip
163
- continue
164
- if not skip:
165
- clean_lines.append(line)
166
- content = '\n'.join(clean_lines)
167
- return content
168
-
169
  def _clean_explanations(self, content: str) -> str:
 
170
  lines = content.split('\n')
171
  clean_lines = []
172
  for line in lines:
@@ -223,32 +216,22 @@ class LocalCerebrasAnonymizer:
223
  unique_indices = sorted(list(set([int(x) for x in indices])))
224
  if unique_indices[0] != 1:
225
  validation_issues.append(f"⚠️ {entity_type} از 01 شروع نشده")
226
-
227
- expected = list(range(1, len(unique_indices) + 1))
228
- if unique_indices != expected:
229
- validation_issues.append(f"⚠️ {entity_type} پیوسته نیست")
230
 
231
  return {
232
  "is_valid": len(validation_issues) == 0,
233
- "issues": validation_issues,
234
- "entity_counts": {
235
- "company": len(set(companies)),
236
- "person": len(set(persons)),
237
- "amount": len(set(amounts)),
238
- "percent": len(set(percents))
239
- }
240
  }
241
 
242
  # ========== رابط کاربری ==========
243
 
244
- anonymizer = LocalCerebrasAnonymizer()
245
 
246
  def create_interface():
247
  custom_css = """
248
  .gradio-container {
249
  font-family: 'Tahoma', 'Arial', sans-serif !important;
250
  direction: rtl;
251
- max-width: 1400px;
252
  margin: 0 auto;
253
  }
254
  .info-box {
@@ -279,19 +262,19 @@ def create_interface():
279
 
280
  gr.Markdown("""
281
  # 🔒 سیستم ناشناس‌سازی متون مالی فارسی
282
- ### 🚀 Qwen 2.5-32B (HuggingFace Spaces)
283
  """)
284
 
285
  gr.Markdown("""
286
  <div class="info-box">
287
- 📊 <strong>مدل:</strong> Qwen2.5-32B-Instruct-Q4_K_M<br>
288
  🌐 <strong>منبع:</strong> HuggingFace Hub<br>
289
- 💾 <strong>حجم:</strong> ~20 GB (Q4 quantization)<br>
290
- ⚡ <strong>سرعت:</strong> بستگی به GPU Spaces دارد
291
  </div>
292
  """)
293
 
294
- status_box = gr.Textbox(label="📋 وضعیت", interactive=False, value="⏳ درحال بارگذاری مدل...")
295
 
296
  load_btn = gr.Button("🤖 بارگذاری مدل", variant="primary", size="lg")
297
 
@@ -300,8 +283,8 @@ def create_interface():
300
  input_text = gr.Textbox(
301
  label="📝 متن ورودی",
302
  placeholder="متن خود را اینجا وارد کنید...",
303
- lines=12,
304
- max_lines=25
305
  )
306
 
307
  with gr.Row():
@@ -311,8 +294,8 @@ def create_interface():
311
  with gr.Column(scale=1):
312
  output_text = gr.Textbox(
313
  label="🎯 متن ناشناس‌سازی شده",
314
- lines=12,
315
- max_lines=25,
316
  elem_classes=["result-box"]
317
  )
318
 
@@ -357,10 +340,10 @@ def create_interface():
357
  quality = result.get("quality_check", {})
358
  quality_md = f"""✅ **کنترل کیفیت:**
359
 
360
- {'✅ موفق' if quality.get('is_valid') else ' مشکل'}
361
  """
362
  if quality.get("issues"):
363
- quality_md += "\n**مشکلات:**\n"
364
  for issue in quality["issues"]:
365
  quality_md += f"• {issue}\n"
366
 
@@ -412,7 +395,7 @@ def create_interface():
412
  gr.Examples(
413
  examples=[
414
  ["ایران خودرو در اسفندماه حدود 23 هزار میلیارد تومان درآمد کسب کرد که 4.58 درصد افزایش داشت."],
415
- ["مجمع پتروشیمی برگزار شد. وانیا نیک تدبیر را بازرس انتخاب کردند."],
416
  ],
417
  inputs=input_text,
418
  label="📚 مثال‌ها"
 
4
  import os
5
  from dataclasses import dataclass
6
  import re
7
+ import torch
8
+ from transformers import AutoTokenizer, AutoModelForCausalLM
9
 
10
  @dataclass
11
  class LocalModelConfig:
12
+ """تنظیمات مدل Qwen2.5-32B"""
13
+ model_id: str = "Qwen/Qwen2.5-32B-Instruct"
14
+ max_tokens: int = 2048
 
15
  temperature: float = 0.3
16
  top_p: float = 0.8
 
 
 
17
 
18
+ class QwenAnonymizer:
19
+ """سیستم ناشناس‌سازی متون مالی فارسی"""
20
 
21
  def __init__(self):
22
  self.config = LocalModelConfig()
23
+ self.tokenizer = None
24
+ self.model = None
25
  self.model_loaded = False
26
 
27
  def load_model(self) -> str:
28
  """بارگذاری مدل از HuggingFace"""
29
  try:
30
+ print(f"🤖 درحال دانلود و بارگذاری مدل...")
31
+ print(f"📦 Model: {self.config.model_id}")
 
32
 
33
+ # بررسی GPU
34
+ device = "cuda" if torch.cuda.is_available() else "cpu"
35
+ print(f"💻 دستگاه: {device}")
 
 
 
 
36
 
37
+ # بارگذاری tokenizer
38
+ print("📝 بارگذاری tokenizer...")
39
+ self.tokenizer = AutoTokenizer.from_pretrained(self.config.model_id)
40
 
41
+ # بارگذاری مدل
42
+ print("🧠 بارگذاری مدل...")
43
+ if device == "cuda":
44
+ # برای GPU
45
+ self.model = AutoModelForCausalLM.from_pretrained(
46
+ self.config.model_id,
47
+ torch_dtype=torch.float16,
48
+ device_map="auto",
49
+ load_in_4bit=True, # 4-bit quantization
50
+ )
51
+ else:
52
+ # برای CPU
53
+ self.model = AutoModelForCausalLM.from_pretrained(
54
+ self.config.model_id,
55
+ torch_dtype=torch.float32,
56
+ device_map="cpu",
57
+ )
58
 
59
+ self.model.eval()
60
  self.model_loaded = True
61
+
62
  print("✅ مدل با موفقیت بارگذاری شد\n")
63
+ return f"✅ مدل آماده است\n💻 دستگاه: {device}\n🧠 پارامترها: 32B"
64
 
65
  except Exception as e:
66
  error_msg = f"❌ خطا: {str(e)}"
 
74
  ⚠️ CRITICAL: در پاسخ نهایی خود، فقط و فقط متن ناشناس‌سازی شده را برگردانید، بدون هیچ توضیح، تحلیل، یا تگ اضافی.
75
 
76
  ## قوانین اندیس‌گذاری:
77
+ 1. ترتیب پیوسته: company-01, company-02, ... | person-01, person-02, ... | amount-01, amount-02, ... | percent-01, percent-02, ...
78
+ 2. ثبات: اگر "همراه اول" → company-01 شد، در تمام متن همان باشد
 
79
 
80
  ## انواع موجودیت:
81
+ - company-XX: شرکت‌ها، بانک‌ها، سازمان‌ها
82
+ - person-XX: نام و نام خانوادگی اشخاص
83
+ - amount-XX: مبالغ - و��حد را حفظ کن
84
+ - percent-XX: درصدها
85
 
86
  ## مثال:
87
  ورودی: ایران خودرو در اسفند 1402 حدود 23 هزار میلیارد درآمد کسب کرد که 4.58 درصد افزایش داشت.
88
  خروجی: company-01 در اسفند 1402 حدود amount-01 درآمد کسب کرد که percent-01 افزایش داشت.
89
 
90
+ ⚠️ فقط متن ناشناس‌شده، بدون هیچ توضیح اضافی."""
91
 
92
  def anonymize_text(self, text: str) -> Dict[str, Any]:
93
  """ناشناس‌سازی متن"""
 
98
  return {"success": False, "error": "متن ورودی خالی است"}
99
 
100
  try:
101
+ print(f"⏳ پردازش متن...")
102
+
103
+ # ایجاد prompt
104
+ system_prompt = self._create_system_prompt()
105
+ user_prompt = text
106
+
107
+ # فرمت پیام برای Qwen
108
  messages = [
109
+ {"role": "system", "content": system_prompt},
110
+ {"role": "user", "content": user_prompt}
111
  ]
112
 
113
+ # تبدیل به متن
114
+ text_input = self.tokenizer.apply_chat_template(
115
+ messages,
116
+ tokenize=False,
117
+ add_generation_prompt=True
118
+ )
119
+
120
+ # Tokenize
121
+ inputs = self.tokenizer(text_input, return_tensors="pt").to(self.model.device)
122
 
123
+ # Generate
124
+ with torch.no_grad():
125
+ outputs = self.model.generate(
126
+ **inputs,
127
+ max_new_tokens=self.config.max_tokens,
128
+ temperature=self.config.temperature,
129
+ top_p=self.config.top_p,
130
+ do_sample=True,
131
+ pad_token_id=self.tokenizer.eos_token_id,
132
+ )
133
 
134
+ # Decode
135
+ response = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
 
 
 
 
 
136
 
137
+ # استخراج جواب (بعد از assistant:)
138
+ if "assistant" in response:
139
+ content = response.split("assistant")[-1].strip()
140
+ else:
141
+ content = response.strip()
142
 
143
  # پاک‌سازی
 
 
144
  content = self._clean_explanations(content)
145
  content = content.strip()
146
 
 
158
  except Exception as e:
159
  return {"success": False, "error": f"خطا: {str(e)}"}
160
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  def _clean_explanations(self, content: str) -> str:
162
+ """حذف توضیحات اضافی"""
163
  lines = content.split('\n')
164
  clean_lines = []
165
  for line in lines:
 
216
  unique_indices = sorted(list(set([int(x) for x in indices])))
217
  if unique_indices[0] != 1:
218
  validation_issues.append(f"⚠️ {entity_type} از 01 شروع نشده")
 
 
 
 
219
 
220
  return {
221
  "is_valid": len(validation_issues) == 0,
222
+ "issues": validation_issues
 
 
 
 
 
 
223
  }
224
 
225
  # ========== رابط کاربری ==========
226
 
227
+ anonymizer = QwenAnonymizer()
228
 
229
  def create_interface():
230
  custom_css = """
231
  .gradio-container {
232
  font-family: 'Tahoma', 'Arial', sans-serif !important;
233
  direction: rtl;
234
+ max-width: 1200px;
235
  margin: 0 auto;
236
  }
237
  .info-box {
 
262
 
263
  gr.Markdown("""
264
  # 🔒 سیستم ناشناس‌سازی متون مالی فارسی
265
+ ### 🚀 Qwen 2.5-32B (HuggingFace)
266
  """)
267
 
268
  gr.Markdown("""
269
  <div class="info-box">
270
+ 📊 <strong>مدل:</strong> Qwen2.5-32B-Instruct<br>
271
  🌐 <strong>منبع:</strong> HuggingFace Hub<br>
272
+ 💾 <strong>حجم:</strong> 32B Parameters<br>
273
+ ⚡ <strong>بهینه‌سازی:</strong> Transformers + PyTorch
274
  </div>
275
  """)
276
 
277
+ status_box = gr.Textbox(label="📋 وضعیت", interactive=False, value="⏳ آماده برای بارگذاری...")
278
 
279
  load_btn = gr.Button("🤖 بارگذاری مدل", variant="primary", size="lg")
280
 
 
283
  input_text = gr.Textbox(
284
  label="📝 متن ورودی",
285
  placeholder="متن خود را اینجا وارد کنید...",
286
+ lines=10,
287
+ max_lines=20
288
  )
289
 
290
  with gr.Row():
 
294
  with gr.Column(scale=1):
295
  output_text = gr.Textbox(
296
  label="🎯 متن ناشناس‌سازی شده",
297
+ lines=10,
298
+ max_lines=20,
299
  elem_classes=["result-box"]
300
  )
301
 
 
340
  quality = result.get("quality_check", {})
341
  quality_md = f"""✅ **کنترل کیفیت:**
342
 
343
+ {'✅ موفق' if quality.get('is_valid') else '⚠️ هشدار'}
344
  """
345
  if quality.get("issues"):
346
+ quality_md += "\n**نکات:**\n"
347
  for issue in quality["issues"]:
348
  quality_md += f"• {issue}\n"
349
 
 
395
  gr.Examples(
396
  examples=[
397
  ["ایران خودرو در اسفندماه حدود 23 هزار میلیارد تومان درآمد کسب کرد که 4.58 درصد افزایش داشت."],
398
+ ["بانک ملی ایران و حسن روحانی در جلسه امروز بحث کردند."],
399
  ],
400
  inputs=input_text,
401
  label="📚 مثال‌ها"