leilaghomashchi commited on
Commit
72a098e
·
verified ·
1 Parent(s): 86a6145

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +120 -138
app.py CHANGED
@@ -4,95 +4,29 @@ from typing import Dict, Any
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)}"
67
- print(error_msg)
68
- return error_msg
69
-
70
- def _create_system_prompt(self) -> str:
71
- """دستورالعمل سیستمی"""
72
- return """شما یک سیستم ناشناس‌سازی متون مالی فارسی هستید.
73
-
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
  """ناشناس‌سازی متن"""
94
- if not self.model_loaded:
95
- return {"success": False, "error": "مدل بارگذاری نشده است"}
96
 
97
  if not text.strip():
98
  return {"success": False, "error": "متن ورودی خالی است"}
@@ -100,45 +34,46 @@ class QwenAnonymizer:
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)
@@ -155,16 +90,36 @@ class QwenAnonymizer:
155
  "quality_check": self._validate_anonymized_text(content)
156
  }
157
 
 
 
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:
166
  if any(word in line.lower() for word in
167
- ['okay', 'let me', 'here is', 'خروجی', 'نتیجه', 'پاسخ:', 'assistant', '[inst]']):
168
  continue
169
  clean_lines.append(line)
170
  return '\n'.join(clean_lines).strip()
@@ -224,9 +179,11 @@ class QwenAnonymizer:
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;
@@ -242,7 +199,7 @@ def create_interface():
242
  color: #0d47a1;
243
  margin: 10px 0;
244
  }
245
- .local-box {
246
  background-color: #e8f5e9;
247
  border: 2px solid #4caf50;
248
  border-radius: 12px;
@@ -250,6 +207,14 @@ def create_interface():
250
  color: #1b5e20;
251
  margin: 10px 0;
252
  }
 
 
 
 
 
 
 
 
253
  .result-box {
254
  background-color: #f8f9fa;
255
  border: 2px solid #e9ecef;
@@ -262,23 +227,28 @@ def create_interface():
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
 
281
- with gr.Row(visible=False) as input_section:
282
  with gr.Column(scale=1):
283
  input_text = gr.Textbox(
284
  label="📝 متن ورودی",
@@ -299,31 +269,29 @@ def create_interface():
299
  elem_classes=["result-box"]
300
  )
301
 
302
- with gr.Row(visible=False) as output_section:
303
  with gr.Column():
304
  statistics_output = gr.Markdown(label="📊 آمار")
305
  with gr.Column():
306
  quality_output = gr.Markdown(label="✅ کیفیت")
307
 
308
- with gr.Row(visible=False) as output_section2:
309
  entities_output = gr.Markdown(label="🏷️ موجودیت‌ها")
310
  detailed_output = gr.Markdown(label="🔍 تحلیل")
311
 
312
- def load_model_action():
313
- """بارگذاری مدل"""
314
- msg = anonymizer.load_model()
315
- return (
316
- gr.Textbox(value=msg),
317
- gr.Row(visible=True),
318
- gr.Row(visible=True),
319
- gr.Row(visible=True)
320
- )
321
-
322
- def process_text(text):
323
  """پردازش متن"""
 
 
 
 
 
324
  if not text.strip():
325
  return ("", "❌ متن خالی است", "", "", "", "")
326
 
 
 
 
327
  result = anonymizer.anonymize_text(text)
328
 
329
  if not result["success"]:
@@ -376,14 +344,9 @@ def create_interface():
376
  def clear_all():
377
  return "", "", "", "", "", ""
378
 
379
- load_btn.click(
380
- fn=load_model_action,
381
- outputs=[status_box, input_section, output_section, output_section2]
382
- )
383
-
384
  anonymize_btn.click(
385
  fn=process_text,
386
- inputs=[input_text],
387
  outputs=[output_text, statistics_output, quality_output, entities_output, detailed_output, status_box]
388
  )
389
 
@@ -401,6 +364,25 @@ def create_interface():
401
  label="📚 مثال‌ها"
402
  )
403
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
404
  return interface
405
 
406
  if __name__ == "__main__":
 
4
  import os
5
  from dataclasses import dataclass
6
  import re
7
+ import requests
 
8
 
9
  @dataclass
10
+ class QwenConfig:
11
+ """تنظیمات Qwen 2.5-32B via HF Inference API"""
12
  model_id: str = "Qwen/Qwen2.5-32B-Instruct"
13
+ api_url: str = "https://api-inference.huggingface.co/models/Qwen/Qwen2.5-32B-Instruct"
14
+ max_tokens: int = 1024
15
  temperature: float = 0.3
16
  top_p: float = 0.8
17
 
18
  class QwenAnonymizer:
19
  """سیستم ناشناس‌سازی متون مالی فارسی"""
20
 
21
+ def __init__(self, hf_token: str = None):
22
+ self.config = QwenConfig()
23
+ self.hf_token = hf_token or os.getenv("HF_TOKEN")
24
+ self.model_loaded = bool(self.hf_token)
 
25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  def anonymize_text(self, text: str) -> Dict[str, Any]:
27
  """ناشناس‌سازی متن"""
28
+ if not self.hf_token:
29
+ return {"success": False, "error": "HF_TOKEN یافت نشد"}
30
 
31
  if not text.strip():
32
  return {"success": False, "error": "متن ورودی خالی است"}
 
34
  try:
35
  print(f"⏳ پردازش متن...")
36
 
 
37
  system_prompt = self._create_system_prompt()
 
38
 
39
+ # ایجاد payload
40
+ payload = {
41
+ "inputs": f"""[INST] {system_prompt}
42
+
43
+ متن ورودی:
44
+ {text}
45
+
46
+ فقط متن ناشناس‌سازی شده را برگردان: [/INST]""",
47
+ "parameters": {
48
+ "max_new_tokens": self.config.max_tokens,
49
+ "temperature": self.config.temperature,
50
+ "top_p": self.config.top_p,
51
+ "do_sample": True,
52
+ "return_full_text": False,
53
+ }
54
+ }
55
 
56
+ # درخواست API
57
+ headers = {"Authorization": f"Bearer {self.hf_token}"}
58
+ response = requests.post(
59
+ self.config.api_url,
60
+ headers=headers,
61
+ json=payload,
62
+ timeout=120
63
  )
64
 
65
+ if response.status_code != 200:
66
+ return {
67
+ "success": False,
68
+ "error": f"خطا از API: {response.status_code} - {response.text}"
69
+ }
 
 
 
 
 
 
 
 
70
 
71
+ result = response.json()
 
72
 
73
+ if isinstance(result, list) and len(result) > 0:
74
+ content = result[0].get("generated_text", "").strip()
 
75
  else:
76
+ content = str(result).strip()
77
 
78
  # پاک‌سازی
79
  content = self._clean_explanations(content)
 
90
  "quality_check": self._validate_anonymized_text(content)
91
  }
92
 
93
+ except requests.exceptions.Timeout:
94
+ return {"success": False, "error": "⏱️ مدل درحال بارگذاری است (۳۰-۶۰ ثانیه صبر کنید)"}
95
  except Exception as e:
96
  return {"success": False, "error": f"خطا: {str(e)}"}
97
 
98
+ def _create_system_prompt(self) -> str:
99
+ """دستورالعمل سیستمی"""
100
+ return """شما یک سیستم ناشناس‌سازی متون مالی فارسی هستید.
101
+
102
+ قوانین اندیس‌گذاری:
103
+ 1. ترتیب پیوسته: company-01, company-02, ... | person-01, person-02, ... | amount-01, amount-02, ... | percent-01, percent-02, ...
104
+ 2. ثبات: اگر "همراه اول" → company-01 شد، در تمام متن همان باشد
105
+
106
+ انواع موجودیت:
107
+ - company-XX: شرکت‌ها، بانک‌ها، سازمان‌ها
108
+ - person-XX: نام و نام خانوادگی اشخاص
109
+ - amount-XX: مبالغ - واحد را حفظ کن
110
+ - percent-XX: درصدها
111
+
112
+ مثال:
113
+ ورودی: ایران خودرو در اسفند 1402 حدود 23 هزار میلیارد درآمد کسب کرد که 4.58 درصد افزایش داشت.
114
+ خروجی: company-01 در اسفند 1402 حدود amount-01 درآمد کسب کرد که percent-01 افزایش داشت."""
115
+
116
  def _clean_explanations(self, content: str) -> str:
117
  """حذف توضیحات اضافی"""
118
  lines = content.split('\n')
119
  clean_lines = []
120
  for line in lines:
121
  if any(word in line.lower() for word in
122
+ ['okay', 'let me', 'here is', 'خروجی', 'نتیجه', 'پاسخ:', 'assistant']):
123
  continue
124
  clean_lines.append(line)
125
  return '\n'.join(clean_lines).strip()
 
179
 
180
  # ========== رابط کاربری ==========
181
 
182
+ anonymizer = None
183
 
184
  def create_interface():
185
+ global anonymizer
186
+
187
  custom_css = """
188
  .gradio-container {
189
  font-family: 'Tahoma', 'Arial', sans-serif !important;
 
199
  color: #0d47a1;
200
  margin: 10px 0;
201
  }
202
+ .success-box {
203
  background-color: #e8f5e9;
204
  border: 2px solid #4caf50;
205
  border-radius: 12px;
 
207
  color: #1b5e20;
208
  margin: 10px 0;
209
  }
210
+ .warning-box {
211
+ background-color: #fff3cd;
212
+ border: 2px solid #ffc107;
213
+ border-radius: 12px;
214
+ padding: 15px;
215
+ color: #856404;
216
+ margin: 10px 0;
217
+ }
218
  .result-box {
219
  background-color: #f8f9fa;
220
  border: 2px solid #e9ecef;
 
227
 
228
  gr.Markdown("""
229
  # 🔒 سیستم ناشناس‌سازی متون مالی فارسی
230
+ ### 🚀 Qwen 2.5-32B (HuggingFace Inference API)
231
  """)
232
 
233
+ hf_token_input = gr.Textbox(
234
+ label="🔑 HuggingFace API Token",
235
+ placeholder="hf_...",
236
+ type="password",
237
+ info="از https://huggingface.co/settings/tokens بگیرید"
238
+ )
239
+
240
  gr.Markdown("""
241
  <div class="info-box">
242
  📊 <strong>مدل:</strong> Qwen2.5-32B-Instruct<br>
243
+ 🌐 <strong>منبع:</strong> HuggingFace Inference API<br>
244
+ <strong>مزیت:</strong> بدون نیاز به نصب • سریع • رایگان<br>
245
+ ⚡ <strong>وضعیت:</strong> آماده برای استفاده فوری
246
  </div>
247
  """)
248
 
249
+ status_box = gr.Textbox(label="📋 وضعیت", interactive=False, value=" آماده")
 
 
250
 
251
+ with gr.Row():
252
  with gr.Column(scale=1):
253
  input_text = gr.Textbox(
254
  label="📝 متن ورودی",
 
269
  elem_classes=["result-box"]
270
  )
271
 
272
+ with gr.Row():
273
  with gr.Column():
274
  statistics_output = gr.Markdown(label="📊 آمار")
275
  with gr.Column():
276
  quality_output = gr.Markdown(label="✅ کیفیت")
277
 
278
+ with gr.Row():
279
  entities_output = gr.Markdown(label="🏷️ موجودیت‌ها")
280
  detailed_output = gr.Markdown(label="🔍 تحلیل")
281
 
282
+ def process_text(text, token):
 
 
 
 
 
 
 
 
 
 
283
  """پردازش متن"""
284
+ global anonymizer
285
+
286
+ if not token or not token.strip():
287
+ return ("", "❌ HF Token الزامی است", "", "", "", "")
288
+
289
  if not text.strip():
290
  return ("", "❌ متن خالی است", "", "", "", "")
291
 
292
+ # ایجاد anonymizer با token
293
+ anonymizer = QwenAnonymizer(hf_token=token.strip())
294
+
295
  result = anonymizer.anonymize_text(text)
296
 
297
  if not result["success"]:
 
344
  def clear_all():
345
  return "", "", "", "", "", ""
346
 
 
 
 
 
 
347
  anonymize_btn.click(
348
  fn=process_text,
349
+ inputs=[input_text, hf_token_input],
350
  outputs=[output_text, statistics_output, quality_output, entities_output, detailed_output, status_box]
351
  )
352
 
 
364
  label="📚 مثال‌ها"
365
  )
366
 
367
+ with gr.Accordion("📖 راهنما", open=False):
368
+ gr.Markdown("""
369
+ ## 🔑 چگونه HF Token بگیرید:
370
+
371
+ 1. به https://huggingface.co/settings/tokens بروید
372
+ 2. **New token** کلیک کنید
373
+ 3. نام انتخاب کنید (مثلاً: qwen-anonymizer)
374
+ 4. **Type: Read** انتخاب کنید
375
+ 5. **Generate** کلیک کنید
376
+ 6. Token رو کپی کنید
377
+
378
+ ## 🚀 چگونه استفاده کنید:
379
+
380
+ 1. Token را در بالا وارد کنید
381
+ 2. متن خود را در جعبه "متن ورودی" بنویسید
382
+ 3. دکمه "🔒 ناشناس‌سازی" را کلیک کنید
383
+ 4. نتیجه در جعبه "متن ناشناس‌سازی شده" نمایش داده می‌شود
384
+ """)
385
+
386
  return interface
387
 
388
  if __name__ == "__main__":