youssefreda9's picture
Cleanup: Archive legacy scripts + phase reports, delete 35 orphaned files
78a516a
Raw
History Blame Contribute Delete
7.72 kB
# -*- 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)