File size: 7,716 Bytes
92557ee | 1 2 3 4 5 6 7 8 9 10 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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | # -*- coding: utf-8 -*-
"""Untitled18.ipynb
Automatically generated by Colab.
Original file is located at
https://colab.research.google.com/drive/1ebBGzEo4wbwwvReea_n0PRHdfYescKcs
"""
import os
import torch
from transformers import EncoderDecoderModel, AutoTokenizer
import re
# تعريف الثوابت
HF_REPO_ID = "bayan10/PuncAra-v1"
# متغيرات عامة
device = None
test_model = None
test_tokenizer = None
def initialize_model(repo_id=HF_REPO_ID):
"""
تهيئة وإعداد كرت الشاشة وتحميل النموذج والـ Tokenizer من Hugging Face Hub.
يتم استدعاء هذه الدالة مرة واحدة فقط في بداية تشغيل المشروع.
"""
global device, test_model, test_tokenizer
print(f"Loading test model directly from Hugging Face Hub: {repo_id}")
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Loading test model to: {device}")
if device.type == "cuda" and not torch.cuda.is_available():
print("Warning: CUDA device requested, but torch.cuda.is_available() is False. Model will be loaded to CPU.")
device = torch.device("cpu")
test_model = EncoderDecoderModel.from_pretrained(repo_id).to(device)
test_tokenizer = AutoTokenizer.from_pretrained(repo_id)
# إعداد الـ Special tokens للـ Decoder والـ Encoder
test_model.config.decoder_start_token_id = test_tokenizer.cls_token_id
test_model.config.bos_token_id = test_tokenizer.cls_token_id
test_model.config.eos_token_id = test_tokenizer.sep_token_id
test_model.config.pad_token_id = test_tokenizer.pad_token_id
print("Model and Tokenizer loaded successfully!")
def predict_chunk(text_chunk):
"""توليد التوقعات لعلامات الترقيم لقطعة نصية صغيرة لا تتعدى الـ 128 Token."""
global device, test_model, test_tokenizer
if test_model is None or test_tokenizer is None:
raise RuntimeError("الموديل لم يتم تهيئته بعد. يرجى استدعاء initialize_model() أولاً.")
# تطبيق الـ Preprocessing لتنظيف التشكيل قبل دخول النص للموديل
text_chunk = arabic_preprocessing(text_chunk)
inputs = test_tokenizer(text_chunk, return_tensors="pt", padding=True, truncation=True, max_length=128).to(device)
outputs = test_model.generate(
inputs.input_ids,
attention_mask=inputs.attention_mask,
decoder_start_token_id=test_tokenizer.cls_token_id,
bos_token_id=test_tokenizer.cls_token_id,
eos_token_id=test_tokenizer.sep_token_id,
pad_token_id=test_tokenizer.pad_token_id,
max_length=128,
num_beams=3,
repetition_penalty=1.2,
length_penalty=1.0,
early_stopping=True,
do_sample=False
)
return test_tokenizer.decode(outputs[0], skip_special_tokens=True)
def arabic_preprocessing(text):
"""حذف الحركات التشكيلية لتوحيد المدخلات وتسهيل عمل الموديل."""
arabic_diacritics = re.compile(r'[\u064B-\u0652]')
return re.sub(arabic_diacritics, '', text).strip()
def arabic_postprocessing(text):
"""
التنظيف والتحسين المطبعي وعلاج مشاكل دمج النصوص وعلامات الترقيم الزائدة.
"""
if not text:
return text
# 1. حماية الأرقام والكسور والتوقيت من التحويل الخاطئ
text = re.sub(r'(?<=\d),(?=\d)', '٪TEMP_COMMA٪', text)
text = re.sub(r'(?<=\d):(?=\d)', '٪TEMP_COLON٪', text)
# 2. التوحيد والتعريب المطبعي للعلامات
text = text.replace(',', '،').replace(';', '؛').replace('?', '؟')
# 3. ضبط المسافات الداخلية للأقواس وعلامات الاقتباس العربي
text = re.sub(r'\(\s+', '(', text)
text = re.sub(r'\s+\)', ')', text)
text = re.sub(r'\[\s+', '[', text)
text = re.sub(r'\s+\]', ']', text)
text = re.sub(r'«\s+', '«', text)
text = re.sub(r'\s+»', '»', text)
# 4. منع تكرار العلامات الانفعالية عدا النقاط الثلاثية للحذف
text = re.sub(r'([،؛:!؟])\1+', r'\1', text)
text = re.sub(r'\.{4,}', '...', text)
# 5. معالجة التناقضات المباشرة الناتجة عن تجميع الـ Chunks
text = re.sub(r'[،؛:]+([.!؟])', r'\1', text)
text = re.sub(r'،؛|؛،', '؛', text)
text = re.sub(r'([!؟])\.', r'\1', text)
# 6. مسح علامات الترقيم العشوائية إذا ظهرت أول النص
text = re.sub(r'^[،؛:!؟. \t]+', '', text)
# 7. ضمان مسافة فارغة واحدة بعد علامة الترقيم إذا تبعها كلام
text = re.sub(r'([،؛:!؟.])(?=\S)', r'\1 ', text)
# 8. إعادة الأرقام والكسور والتوقيت المحمية إلى أصلها
text = text.replace('٪TEMP_COMMA٪', ',').replace('٪TEMP_COLON٪', ':')
# 9. إلصاق علامات الترقيم بالكلمة السابقة لها مباشرة
text = re.sub(r'\s+([،؛:!؟.])', r'\1', text)
# 10. إزالة المسافات المتكررة الأفقية فقط (بدون لمس السطور الجديدة)
text = re.sub(r'[ \t]+', ' ', text).strip()
return text
def fix_punctuation(text):
"""معالجة الفقرة الواحدة الطويلة عبر تقسيمها لقطع غير متداخلة لمنع التكرار."""
words = text.split()
total_words = len(words)
# جعل حجم الخطوة مساوياً لحجم النافذة يمنع تكرار الكلمات تماماً
window_size = 50
stride = 50
if total_words <= window_size:
result = predict_chunk(text)
else:
segments_output = []
for i in range(0, total_words, stride):
chunk_words = words[i : i + window_size]
chunk_text = " ".join(chunk_words)
if not chunk_text.strip():
continue
processed_segment = predict_chunk(chunk_text).strip()
# مسح علامات الترقيم الناتجة عن القص الإجباري بين القطع
is_last_segment = (i + window_size) >= total_words
if not is_last_segment:
punctuation_marks = ".?!،؛:؟!"
if processed_segment and processed_segment[-1] in punctuation_marks:
# نمسح العلامة تماماً لأن السياق مستمر في القطعة اللي بعدها
processed_segment = processed_segment[:-1]
segments_output.append(processed_segment)
result = " ".join(segments_output)
# تنظيف المسافات الزائدة والتكرار إن وجد
result = re.sub(r'\s+', ' ', result).strip()
return result
def process_full_document(text):
if not text:
return text
# تقسيم بناءً على السطور الجديدة وتنظيف الأسطر الفارغة
paragraphs = [p.strip() for p in text.split('\n') if p.strip()]
processed_paragraphs = []
for paragraph in paragraphs:
# معالجة الفقرة المستقلة
punctuated_paragraph = fix_punctuation(paragraph)
cleaned_paragraph = arabic_postprocessing(punctuated_paragraph)
processed_paragraphs.append(cleaned_paragraph)
# الدمج بسطرين متباعدين لضمان الفصل البصري التام بين الفقرات
return "\n\n".join(processed_paragraphs) |