import pandas as pd import re import numpy as np import json from typing import Dict, List, Any, Tuple import gradio as gr from pathlib import Path import plotly.graph_objects as go import plotly.express as px from dataclasses import dataclass from datetime import datetime @dataclass class BenchmarkMetrics: """کلاس متریک‌های بنچمارک""" model_name: str total_texts: int total_entities: int accuracy: float # درستی کلی ناشناس‌سازی recall: float # پوشش موجودیت‌ها precision: float # دقت شناسایی class AnonymizationBenchmark: """کلاس اصلی بنچمارک ناشناس‌سازی""" def __init__(self): self.models_data = {} self.benchmark_results = {} def load_csv_files(self, chatgpt_file, grok_file, llama_file): """بارگذاری فایل‌های CSV""" try: # بارگذاری فایل‌ها chatgpt_df = pd.read_csv(chatgpt_file) grok_df = pd.read_csv(grok_file) llama_df = pd.read_csv(llama_file) # بررسی ستون‌ها required_columns = ['original_text', 'anonymized_text'] for df_name, df in [('ChatGPT', chatgpt_df), ('Grok', grok_df), ('Llama', llama_df)]: if not all(col in df.columns for col in required_columns): raise ValueError(f"فایل {df_name} فاقد ستون‌های مورد نیاز است") self.models_data = { 'ChatGPT': chatgpt_df, 'Grok': grok_df, 'Llama-3.1-8B': llama_df } return True, "فایل‌ها با موفقیت بارگذاری شدند" except Exception as e: return False, f"خطا در بارگذاری فایل‌ها: {str(e)}" def extract_entities_from_text(self, text: str) -> Dict[str, List[str]]: """استخراج موجودیت‌ها از متن""" entities = { 'companies': re.findall(r'company-(\d+)', text), 'persons': re.findall(r'person-(\d+)', text), 'amounts': re.findall(r'amount-(\d+)', text), 'percents': re.findall(r'percent-(\d+)', text), 'groups': re.findall(r'group-(\d+)', text) } return entities def count_original_entities(self, text: str) -> int: """تخمین تعداد موجودیت‌های قابل ناشناس‌سازی در متن اصلی""" # الگوهای شناسایی موجودیت‌ها در متن فارسی patterns = [ r'[۰-۹]+(?:\.[۰-۹]+)?\s*(?:میلیارد|میلیون|هزار)?\s*(?:تومان|ریال|دلار|یورو)', # اعداد پولی r'[۰-۹]+(?:\.[۰-۹]+)?\s*درصد', # درصدها r'\b[آ-ی\s]{2,30}\b(?:\s*(?:شرکت|بانک|گروه|سازمان))', # شرکت‌ها r'\b[آ-ی\s]{2,20}\b(?:\s*(?:مدیرعامل|رئیس|مدیر))', # اشخاص r'[۰-۹]+(?:\.[۰-۹]+)?(?:\s*(?:میلیون|میلیارد|هزار))?', # سایر اعداد ] total_entities = 0 for pattern in patterns: matches = re.findall(pattern, text) total_entities += len(matches) return max(total_entities, 1) # حداقل 1 برای جلوگیری از تقسیم بر صفر def check_indexing_correctness(self, entities: Dict[str, List[str]]) -> float: """بررسی درستی اندیس‌گذاری""" total_checks = 0 passed_checks = 0 for entity_type, indices in entities.items(): if not indices: continue total_checks += 1 unique_indices = sorted([int(x) for x in set(indices)]) # بررسی شروع از 1 if unique_indices[0] == 1: passed_checks += 0.5 # بررسی پیوستگی expected = list(range(1, len(unique_indices) + 1)) if unique_indices == expected: passed_checks += 0.5 return passed_checks / total_checks if total_checks > 0 else 0.0 def calculate_structure_preservation(self, original_text: str, anonymized_text: str) -> float: """محاسبه امتیاز حفظ ساختار""" # کلمات مهم که باید حفظ شوند important_words = [ 'میلیارد', 'میلیون', 'تومان', 'ریال', 'درصد', 'سود', 'زیان', 'مدیرعامل', 'شرکت', 'بانک', 'درآمد', 'سال', 'ماه' ] score = 0.0 total_checks = len(important_words) for word in important_words: if word in original_text and word in anonymized_text: score += 1.0 elif word not in original_text: total_checks -= 1 # بررسی حفظ تعداد کلمات (تقریبی) original_words = len(original_text.split()) anonymized_words = len(anonymized_text.split()) if original_words > 0: word_ratio = min(anonymized_words / original_words, 1.0) score += word_ratio * 2 total_checks += 2 return score / total_checks if total_checks > 0 else 0.0 def calculate_accuracy(self, original_text: str, anonymized_text: str) -> float: """محاسبه درستی کلی ناشناس‌سازی""" entities = self.extract_entities_from_text(anonymized_text) # بررسی درستی اندیس‌گذاری indexing_score = self.check_indexing_correctness(entities) # بررسی حفظ ساختار structure_score = self.calculate_structure_preservation(original_text, anonymized_text) # میانگین وزنی accuracy = (indexing_score * 0.6) + (structure_score * 0.4) return accuracy def calculate_recall(self, original_text: str, anonymized_text: str) -> float: """محاسبه پوشش موجودیت‌ها (Recall)""" original_entity_count = self.count_original_entities(original_text) entities = self.extract_entities_from_text(anonymized_text) anonymized_entity_count = sum(len(set(v)) for v in entities.values()) return min(anonymized_entity_count / original_entity_count, 1.0) def calculate_precision(self, anonymized_text: str) -> float: """محاسبه دقت شناسایی (Precision)""" entities = self.extract_entities_from_text(anonymized_text) # بررسی کیفیت موجودیت‌های شناسایی شده total_entities = sum(len(v) for v in entities.values()) if total_entities == 0: return 0.0 # بررسی درستی فرمت شناسه‌ها correct_entities = 0 for entity_type, indices in entities.items(): for idx in indices: if idx.isdigit() and int(idx) > 0: correct_entities += 1 # بررسی عدم تکرار غیرضروری unique_entities = sum(len(set(v)) for v in entities.values()) consistency_bonus = unique_entities / total_entities if total_entities > 0 else 0 base_precision = correct_entities / total_entities if total_entities > 0 else 0 return (base_precision * 0.7) + (consistency_bonus * 0.3) def analyze_model(self, model_name: str, df: pd.DataFrame) -> BenchmarkMetrics: """تحلیل یک مدل""" print(f"تحلیل مدل {model_name}...") total_texts = len(df) # محاسبه متریک‌ها برای هر متن accuracy_scores = [] recall_scores = [] precision_scores = [] total_entities = 0 for _, row in df.iterrows(): original = str(row['original_text']) anonymized = str(row['anonymized_text']) # محاسبه متریک‌ها accuracy_scores.append(self.calculate_accuracy(original, anonymized)) recall_scores.append(self.calculate_recall(original, anonymized)) precision_scores.append(self.calculate_precision(anonymized)) # شمارش موجودیت‌ها entities = self.extract_entities_from_text(anonymized) total_entities += sum(len(set(v)) for v in entities.values()) return BenchmarkMetrics( model_name=model_name, total_texts=total_texts, total_entities=total_entities, accuracy=round(np.mean(accuracy_scores), 3), recall=round(np.mean(recall_scores), 3), precision=round(np.mean(precision_scores), 3) ) def run_benchmark(self) -> Tuple[bool, str, str]: """اجرای بنچمارک کامل""" if not self.models_data: return False, "ابتدا فایل‌ها را بارگذاری کنید", "" try: results = {} # تحلیل هر مدل for model_name, df in self.models_data.items(): results[model_name] = self.analyze_model(model_name, df) self.benchmark_results = results # تولید HTML html_report = self.generate_html_report() return True, "بنچمارک با موفقیت انجام شد", html_report except Exception as e: return False, f"خطا در اجرای بنچمارک: {str(e)}", "" def generate_comparison_table(self) -> str: """تولید جدول مقایسه""" if not self.benchmark_results: return "

هنوز بنچمارکی انجام نشده است

" # آماده‌سازی داده‌ها برای جدول table_data = [] for model_name, metrics in self.benchmark_results.items(): table_data.append({ 'مدل': model_name, 'تعداد متن‌ها': metrics.total_texts, 'کل موجودیت‌ها': metrics.total_entities, '🎯 دقت (Accuracy)': f"{metrics.accuracy:.3f}", '📊 بازیابی (Recall)': f"{metrics.recall:.3f}", '✅ دقت شناسایی (Precision)': f"{metrics.precision:.3f}" }) # تولید HTML جدول html = """
""" # سرستون‌ها headers = list(table_data[0].keys()) for header in headers: html += f"" html += "" # ردیف‌ها for i, row in enumerate(table_data): bg_color = "#f2f2f2" if i % 2 == 0 else "white" html += f"" for j, (key, value) in enumerate(row.items()): # رنگ‌بندی ستون‌های متریک‌ها if key in ['🎯 دقت (Accuracy)', '📊 بازیابی (Recall)', '✅ دقت شناسایی (Precision)']: score = float(value) if score >= 0.8: color = "#4CAF50" # سبز elif score >= 0.6: color = "#FF9800" # نارنجی else: color = "#F44336" # قرمز html += f"" else: html += f"" html += "" html += "
{header}
{value}{value}
" return html def generate_charts(self) -> str: """تولید نمودارها""" if not self.benchmark_results: return "" models = list(self.benchmark_results.keys()) accuracy_scores = [self.benchmark_results[model].accuracy for model in models] recall_scores = [self.benchmark_results[model].recall for model in models] precision_scores = [self.benchmark_results[model].precision for model in models] # نمودار مقایسه سه متریک chart_html = """

مقایسه متریک‌های عملکرد مدل‌ها

""" colors = ['#4CAF50', '#2196F3', '#FF9800'] for i, model in enumerate(models): accuracy = accuracy_scores[i] recall = recall_scores[i] precision = precision_scores[i] chart_html += f"""

{model}

Accuracy
{accuracy:.3f}
Recall
{recall:.3f}
Precision
{precision:.3f}
""" chart_html += "
" return chart_html def generate_html_report(self) -> str: """تولید گزارش HTML کامل""" current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") html = f""" گزارش بنچمارک ناشناس‌سازی

🏆 گزارش بنچمارک ناشناس‌سازی

مقایسه عملکرد مدل‌های ChatGPT، Grok و Llama-3.1-8B

📋 خلاصه نتایج

این گزارش نتایج بنچمارک سه مدل مختلف برای ناشناس‌سازی متون فارسی را نشان می‌دهد. متریک‌های ارزیابی شامل درستی اندیس‌گذاری، ثبات استفاده از شناسه‌ها، حفظ ساختار متن و پوشش موجودیت‌ها می‌باشد.

📊 جدول مقایسه کامل

{self.generate_comparison_table()}

📈 نمودار مقایسه

{self.generate_charts()}

🔍 تحلیل تفصیلی

{self.generate_detailed_analysis()}
""" return html def generate_detailed_analysis(self) -> str: """تولید تحلیل تفصیلی""" if not self.benchmark_results: return "

داده‌ای برای تحلیل یافت نشد

" # یافتن بهترین مدل در هر متریک best_accuracy = max(self.benchmark_results.keys(), key=lambda k: self.benchmark_results[k].accuracy) best_recall = max(self.benchmark_results.keys(), key=lambda k: self.benchmark_results[k].recall) best_precision = max(self.benchmark_results.keys(), key=lambda k: self.benchmark_results[k].precision) # محاسبه میانگین avg_accuracy = np.mean([m.accuracy for m in self.benchmark_results.values()]) avg_recall = np.mean([m.recall for m in self.benchmark_results.values()]) avg_precision = np.mean([m.precision for m in self.benchmark_results.values()]) analysis = f"""
🎯
بهترین Accuracy: {best_accuracy}
{self.benchmark_results[best_accuracy].accuracy:.3f}
📊
بهترین Recall: {best_recall}
{self.benchmark_results[best_recall].recall:.3f}
بهترین Precision: {best_precision}
{self.benchmark_results[best_precision].precision:.3f}

📈 آمار کلی:

💡 تفسیر نتایج:

""" return analysis # رابط کاربری Gradio def create_benchmark_interface(): """ایجاد رابط کاربری بنچمارک""" benchmark = AnonymizationBenchmark() with gr.Blocks( title="بنچمارک ناشناس‌سازی", theme=gr.themes.Soft(), css=""" .gradio-container { font-family: 'Tahoma', 'Arial', sans-serif !important; direction: rtl; max-width: 1400px; margin: 0 auto; } .upload-box { border: 2px dashed #4CAF50; border-radius: 10px; padding: 20px; text-align: center; background-color: #f8f9fa; margin: 10px 0; } """ ) as interface: gr.Markdown(""" # 🏆 ابزار بنچمارک ناشناس‌سازی متون فارسی ### مقایسه عملکرد مدل‌های ChatGPT، Grok و Llama-3.1-8B در ناشناس‌سازی متون مالی/خبری """) with gr.Row(): with gr.Column(scale=1): gr.Markdown("### 📁 بارگذاری فایل‌های CSV") chatgpt_file = gr.File( label="📄 فایل ChatGPT", file_types=[".csv"], elem_classes=["upload-box"] ) grok_file = gr.File( label="📄 فایل Grok", file_types=[".csv"], elem_classes=["upload-box"] ) llama_file = gr.File( label="📄 فایل Llama-3.1-8B", file_types=[".csv"], elem_classes=["upload-box"] ) load_btn = gr.Button( "📂 بارگذاری فایل‌ها", variant="primary", size="lg" ) benchmark_btn = gr.Button( "🚀 اجرای بنچمارک", variant="secondary", size="lg", interactive=False ) with gr.Column(scale=2): status_output = gr.Markdown("وضعیت: آماده بارگذاری فایل‌ها") results_html = gr.HTML( label="📊 نتایج بنچمارک", visible=False ) def load_files(chatgpt, grok, llama): if not all([chatgpt, grok, llama]): return "❌ لطفاً هر سه فایل را انتخاب کنید", gr.Button(interactive=False), gr.HTML(visible=False) success, message = benchmark.load_csv_files( chatgpt.name, grok.name, llama.name ) if success: return ( f"✅ {message}", gr.Button(interactive=True), gr.HTML(visible=False) ) else: return ( f"❌ {message}", gr.Button(interactive=False), gr.HTML(visible=False) ) def run_benchmark(): success, message, html_report = benchmark.run_benchmark() if success: return ( f"✅ {message}", gr.HTML(value=html_report, visible=True) ) else: return ( f"❌ {message}", gr.HTML(visible=False) ) # اتصال رویدادها load_btn.click( fn=load_files, inputs=[chatgpt_file, grok_file, llama_file], outputs=[status_output, benchmark_btn, results_html] ) benchmark_btn.click( fn=run_benchmark, outputs=[status_output, results_html] ) # راهنمای استفاده with gr.Accordion("📖 راهنمای استفاده", open=False): gr.Markdown(""" ### نحوه استفاده: 1. **بارگذاری فایل‌ها:** سه فایل CSV مربوط به نتایج ناشناس‌سازی مدل‌های مختلف را انتخاب کنید 2. **بررسی فرمت:** هر فایل باید دارای ستون‌های `original_text` و `anonymized_text` باشد 3. **اجرای بنچمارک:** روی دکمه "اجرای بنچمارک" کلیک کنید 4. **مشاهده نتایج:** گزارش HTML کامل با جداول و نمودارها نمایش داده می‌شود ### متریک‌های ارزیابی: - **درستی اندیس‌گذاری:** بررسی شروع از 01 و پیوستگی شماره‌ها - **ثبات شناسه‌ها:** استفاده مداوم از یک شناسه برای یک موجودیت - **حفظ ساختار:** حفظ واژگان مهم و ساختار جمله - **پوشش موجودیت‌ها:** درصد موجودیت‌های شناسایی و ناشناس شده - **امتیاز کلی:** ترکیب وزنی همه متریک‌ها """) return interface # اجرای برنامه if __name__ == "__main__": interface = create_benchmark_interface() interface.launch()