اذهب إلى المحتوى

تلخيص النصوص باستخدام الذكاء الاصطناعي


هدى جبور

غالبًا ما نحتاج في التطبيقات العمليّة إلى تلخيص النصوص لاستخلاص المعلومات المفيدة من المستندات أو الوثائق المختلفة. بدءًا من المقالات البحثية المُعقّدة وتقارير الأرباح المالية إلى رسائل البريد الإلكتروني الطويلة والمقالات الإخباريّة. ويتطلّب تلخيص النصوص فهمًا دقيقًا للمقاطع الطويلة، والقدرة على تحليل محتواها المعقد، والكفاءة في صياغة نص موجز متماسك يُلخّص الموضوعات الأساسية للوثيقة الأصلية.

خلال هذه المقالة سوف:

  • نبحث عن نص لكي نُنفّذ عملية التلخيص عليه باستخدام تقنيات الذكاء الاصطناعي.
  • نتعرّف على أفضل النماذج الموجودة حاليًّأ لتنفيذ هذه المهمة.
  • نكتب بعض التعليمات البرمجية لتنفيذ ذلك.
  • نُقيّم أداء النماذج باستخدام أدوات تقييم الأداء ذات الصلة بهذه المهمة.

النص الذي سنؤدي عليه المهام الموضحة أعلاه هو عبارة عن نص يوضح مجموعة من الاعتبارات والمبادئ الأساسية لتطوير ونشر أنظمة الذكاء الاصطناعي، وقد جلبناه من أحد المواقع يدويًّا للتجريب وتبسيط العمل، لكن في التطبيقات العمليّة، قد نحتاج إلى استخراج هكذا نصوص من مواقع الويب، وهذا يتطلب استخدام أدوات ومكتبات إضافية أخرى.

import textwrap

content = """Mozilla's "Trustworthy AI" Thinking Points:

PRIVACY: How is data collected, stored, and shared? Our personal data powers everything from traffic maps to targeted advertising. Trustworthy AI should enable people to decide how their data is used and what decisions are made with it.

FAIRNESS: We’ve seen time and again how bias shows up in computational models, data, and frameworks behind automated decision making. The values and goals of a system should be power aware and seek to minimize harm. Further, AI systems that depend on human workers should protect people from exploitation and overwork.

TRUST: People should have agency and control over their data and algorithmic outputs, especially considering the high stakes for individuals and societies. For instance, when online recommendation systems push people towards extreme, misleading content, potentially misinforming or radicalizing them.

SAFETY: AI systems can carry high risk for exploitation by bad actors. Developers need to implement strong measures to protect our data and personal security. Further, excessive energy consumption and extraction of natural resources for computing and machine learning accelerates the climate crisis.

TRANSPARENCY: Automated decisions can have huge personal impacts, yet the reasons for decisions are often opaque. We need to mandate transparency so that we can fully understand these systems and their potential for harm."""

نحن الآن جاهزون للبدء بتلخيص هذا النص. لكن دعنا قبل ذلك نناقش بداية أبرز التحديات والحلول المرتبطة بتقنيات الذكاء الاصطناعي.

التحديات والحلول في الوضع الراهن للذكاء الاصطناعي

يتطلّب الوضع الراهن في الذكاء الاصطناعي أن يبقى مهندس الذكاء الاصطناعي يقظًا ومتابعًا لأهم الأعمال والتقنيات والأوراق البحثيّة الجديدة، فكل أسبوع نشاهد العديد من الاختراقات في هذا المجال، فمثلًا التقنيات التي كانت تُستخدم مع مهمةً ما منذ سنة، أصبحت الآن قديمة بكل معنى الكلمة، فربما كنت تستخدم فرضًا نموذج GPT 3 في مشروعك، لكن في لحظة كتابة هذا المقال لدينا GPT 3.5 و GPT 4، وهما نموذجان يُمثلان قفزة هائلة عن GPT 3 وفي المستقبل القريب ستظهر بالتأكيد نماذج أخرى أحدث وأكثر استقرارًا وكفاءة. نستنتج من ذلك أن البيئة الحاليّة للذكاء الاصطناعي هي بيئة ديناميكيّة تتغيّر باستمرار، لذا لن يكون من السهل على المهندسين الجدد المهتمين بهذا المجال مواكبة تلك التطورات السريعة، لذا فالأمر يتطلّب اجتهادًا وصبرًا طويلين، وهذا يتضمّن:

  1. التعرّف على النماذج مفتوحة المصدر المتوفرة.
  2. معرفة مدى مُلاءمة النماذج للمهام المحددة. مثلًا إذا كنت تعمل على مهام تتضمّن توليد اللغة مثل توليد النص أو تلخيصه، فالأفضل غالبًا هي النماذج التي تعتمد على بنية مُفكك التشفير Decoder من نموذج المحولات Transformers مثل GPT أو CTRL، أو بنيتي مُفكك التشفير والمُشفّر Encoder معًا كما في T5.
  3. فهم المعايير والمقاييس المستخدمة لتقييم هذه النماذج فبعض المقاييس مُصمّمة لمهام محددة فقط.
  4. معرفة النماذج التي تُحقّق أفضل النتائج على المهمة المطلوبة، وذلك وفقًا للمقاييس والمعايير النموذجيّة للمهمة المدروسة.
  5. ضمان التوافق مع الأجهزة المتوفرة، مثلًا ChatGPT لايدعم الآندرويد لحظة كتابة هذا المقال.

أما بالنسبة للمهندسين المُنخرطين بالفعل في سوق العمل ويعملون في ظل مواعيد نهائية ضيقة، فربما سيكون من الصعب جدًا عليهم مواكبة أحدث التطورات، والذي يزيد من تعقيد المشكلة هو كثرة المواقع والمجلات والمؤتمرات التي تُنشر فيها أحدث التطورات أو يجري التحدث فيها عن أحدث التطورات، وهذا ما يجعل من الصعب على المهندسين الوصول إلى معلومات شاملة وموحدة.

كيف يمكنني معرفة النماذج مفتوحة المصدر المتاحة لمهمة تلخيص النصوص؟

بالعودة لمهمة تلخيص النص التي تحدثنا عنها في بداية المقال مبدئيًّا، نوصي باستكشاف مكتبة HuggingFace التي تتضمّن دليلًا شاملًا للنماذج مفتوحة المصدر مُصنّفةً حسب المهمة، فهي ممتازة كنقطة انطلاق.

يمكنك الدخول إلى هذا الرابط على سبيل المثال ورؤية العديد من النماذج الخاصة بهذه المهمة. طبعًا بعض هذه النماذج التي ستراها في القائمة بالتأكيد ستتضمّن نماذج اللغة الكبيرة LLMs، وكما تعلم فهذه النماذج تكون مصممة للأغراض العامة (النماذج اللغوية تُصمّم للتعامل مع العديد من المهام) وليس فقط مهمة تصنيف النص، لذا قد لاترغب بذلك، أو إذا كنت ترغب بذلك (الحل الأفضل)، فيتوجّب عليك تكييفها أو ضبطها Fine-tune على مهمة التلخيص المدروسة إن لم تكن مضبوطة عليها مُسبقًا.

لكن في ظل هذا العدد الكبير جدًا من النماذج، قد تنتساءل: ما هي النماذج التي يجب أن أختارها؟ وتزداد الحيرة أكثر بسبب كون هذه النماذج مُدرّبة على أنواع مُختلفة من البيانات، فأحدها ربما يكون مُدرّبًا على تلخيص النشرات الإخباريّة، بالتالي يكون مُدرّبًا على بيانات تتضمّن نشرات أخبار؛ هذا يعني أن هذا النموذج يصلح فقط لتلخيص النصوص الإخباريّة وليس أي نوع آخر، والأسوأ أن بعض النماذج غير معروف ما نوع البيانات المُدرّبة عليها. والإجابة عن هذا السؤال هي "استخدام المقاييس metrics" التي تقيس جودة النموذج بالنسبة للمهمة المطلوبة.

كيف يمكننا تقييّم أداء نماذج تلخيص النصوص؟

يمكن أن تكون الخطوات التالية بمثابة إطار عام لتقييم أي نموذج كان بغض النظر عن المهمة المدروسة، أي سواء كانت مهمة تلخيص النص أم غيرها. تجدر الملاحظة أنّه من أجل أن يكون التقييم دقيقًا، يجب علينا اختبار أداء النموذج على أكثر من مجموعة بيانات، لكن من أجل البساطة، سنجرّب حاليًا على مجموعة بيانات واحدة فقط.

  1. ابحث عن مجموعة بيانات معياريّة (نموذجيّة) تُستخدم في تقييم النموذج المدروس وفقًا للمهمة المطلوبة. طبعًا عندما نقول نموذجيّة أو معياريّة، فهذا يعني أنها مُعتمدة وموثوقة وصالحة وخالية من التحيزات (إلى أكبر حد ممكن) وذات جودة عالية، وذلك لتتماشى مع مبادئ الاستخدام المسؤول للذكاء الاصطناعي. على سبيل المثال يكمن استخدام موقع Paper with code، فهو يوفر أفضل النماذج ومجموعات البيانات التي يُجري الباحثون تقييماتهم عليها. مثلًا هنا يمكنك أن تجد أبرز المجموعات المناسبة لمهمة تلخيص النص (فمجموعات البيانات التي ستراها في الرابط وفي الموقع عمومًا هي مجموعات بيانات معياريّة).
  2. التعرّف على المقاييس المُستخدمة لتقييم المهمة المطروحة. طبعًا هناك مقاييس مُحددة لكل مهمة، وقد يكون مقياسًا ما صالحًا لمهمة واحدة أو عدة مهام.
  3. إجراء التقييم باستخدام مقياس واحد أو أكثر، وعلى مجموعة بيانات واحدة أو أكثر (يُفضّل أكثر من واحدة).

العثور على مجموعات البيانات

كما ذكرت في الفقرة السابقة؛ أفضل طريقة هي الذهاب إلى موقع Papers With Code، فهو موقع موثوق تٌنشر فيه أبرز الأوراق البحثيّة في الذكاء الاصطناعي مع مستودعات الشيفرات البرمجية المرتبطة بها.

اذهب أولاً إلى الموقع ثم إلى انتقل لقسم Datasets، ثم ابحث عن "Text Summarization" ثم انتقل إلى قسم Datasets واضغط على "توسيع أو See all" ومن هناك تظهر جميع مجموعات البيانات ذات الصلة مع إمكانية فرزها أو تصفيتها وفق رغبتك. في حالتنا نريد أن نفرزها حسب عدد مرات الاقتباس cite.

نختار الآن مجموعة البيانات الأكثر اقتباسًا والتي غالبًا ماتكون الأكثر شعبية، وهي مجموعة بيانات "CNN/DailyMail" في وقت كتابة هذه المقالة. لاداعي لتحميل مجموعة البيانات حاليًّا، وإنما سنراجع المعلومات المُتعلقة بمجموعة البيانات هذه والموجودة على موقع Papers With Code، وذلك من أجل الخطوة التالية. تجدر الملاحظة إلى أن مجموعة البيانات هذه متاحة أيضًا على Huggingface.

الآن عليك التحقق من 3 أشياء:

  • الترخيص license: مجموعة البيانات مُرخصة من معهد ماساتشوستس للتكنولوجيا MIT، أي يمكننا استخدامها لكل من المشاريع التجارية والشخصية.
  • الأوراق البحثية الأحدث على هذه البيانات: وذلك لمعرفة فيما إذا كانت الأوراق البحثية التي تستخدم هذه البيانات حديثة أم قديمة. لمعرفة ذلك نفرز الأوراق تنازليًّا. في حالتنا فإن الأوراق التي تستخدم هذه البيانات حديثة (من عام 2023)، وهذا مانرغب به!
  • مدى توفر المعلومات المتعلقة بأصل مجموعة البيانات وعملية جمعها وشفافية الأساليب (مدى صراحة وشمولية توثيق الأساليب المستخدمة لجمع ومعالجة وتنظيم مجموعة البيانات) المستخدمة في إنشائها: مجموعة البيانات مُنشئة من قبل شركة IBM بالشراكة مع جامعة مونتريال، وذلك عظيم! فهذه مصادر موثوقة تلتزم بهذه المتطلبات.

دعونا الآن نستكشف المقاييس المُستخدمة في تقييم أداء النماذج على مجموعة البيانات هذه.

تقييم النماذج

في هذه المرحلة يجب علينا استكشاف المقاييس التي يُشاع استخدامها من قبل الباحثين في تقييّم نماذج التلخيص على مجموعات البيانات. طبعًا يُفترض أن يكون لديك فهم نظري لمهمة تلخيص النص، وإلا فلن تكون قادرًا على فهم هذه المقاييس.

هناك نوعان مختلفان لمهمة تلخيص النص (يمكن اعتبار كل نوع بمثابة مهمة فرعيّة)، النوع الأول هو "تلخيّص النص التجريدي Abstractive Text Summarization" والثاني "تلخيّص النص الاستخراجي Extractive Text Summarization" في النوع الأول يكون الهدف هو تلخيص النص من حيث المعنى، وهذا يعني أن يكون لدينا جمل جديدة، أما في النوع الثاني فيكون عبارة عن استخلاص مقتطفات بسيطة من النص. بالنسبة لمجموعة البيانات التي سنعمل عليها (مقالات CNN)، فهي مرتبطة بالنوع الأول. نلاحظ هنا العديد من المصطلحات وهي ROUGE-1 و ROUGE-2 و ROUGE-3، وهي المقاييس الشائعة لهذه المهمة الفرعية، كما نلاحظ العديد من النماذج ونتائجها وفقًا لهذه المقاييس الثلاث، وهو ما نحتاجه. الآن إذا قارنا بين هذه النماذج وفقًا للمقياس الأول، فنلاحظ أن أول 3 نماذج نتائجها متقاربة، حيث نلاحظ أن جميعها تقترب من 50 وهي قيمة واعدة وفقًا لمقياس ROUGE.

اختبار النموذج

بعد أن تعرّفنا على المقاييس التي يجب أخذها بعين الاعتبار لتقييم النماذج ورؤيتنا لأفضل ثلاثة منها، يمكننا الانتقال إلى مرحلة اختبار النماذج. تجدر الملاحظة أن هذه النماذج يُفضّل أن تُشغّل على وحدة معالجة الرسومات GPU لكونها تنفذ المهام بصورة متزامنة لتحسين الأداء (وبعضها قد لا يعمل إلا عليها)، لكن يمكن أن يكون بعضها قادرًا على العمل على وحدات CPU وبأداء مقبول. دعونا نختار نموذج Pegasus من بين تلك الخيارات، والأمر الذي يجعل اختيارنا موفقًا هو أن هذا النموذج جرى تدريبه على مجموعة بيانات CNN التي نعمل معها بالنسبة لمهمة "استخلاص النص التجريدي".

بعد ذلك لنكتب الكود التالي بلغة البرمجة بايثون والذي يستخدم إطار عمل باي توش PyTorch للغة بايثون ومكتبة Transformers من Hugging Face ونموذج Pegasus لتلخيص النصوص:

# We start by installing the transformers library
%pip install transformers sentencepiece

بعد ذلك علينا الوصول إلى نموذج Pegasus من مكتبة المحولات (مكتبة Huggingface):

from transformers import PegasusForConditionalGeneration, PegasusTokenizer 
import torch 

# Set the seed, this will help reproduce results. Changing the seed will 
# generate new results 
from transformers import set_seed 
set_seed(248602) 

# We're using the version of Pegasus specifically trained for summarization 
# using the CNN/DailyMail dataset 
model_name = "google/pegasus-cnn_dailymail"

# If you're following along in Colab, switch your runtime to a
# T4 GPU or other CUDA-compliant device for a speedup
device = "cuda" if torch.cuda.is_available() else "cpu" 

# Load the tokenizer
tokenizer = PegasusTokenizer.from_pretrained(model_name) 

# Load the model 
model = PegasusForConditionalGeneration.from_pretrained(model_name).to(device)

# Tokenize the entire content
batch = tokenizer(content, padding="longest", return_tensors="pt").to(device)

# Generate the summary as tokens
summarized = model.generate(**batch)

# Decode the tokens back into text
summarized_decoded = tokenizer.batch_decode(summarized, skip_special_tokens=True)
summarized_text = summarized_decoded[0]

# Compare
def compare(original, summarized_text):
  print(f"Article text length: {len(original)}\n")
  print(textwrap.fill(summarized_text, 100))
  print()
  print(f"Summarized length: {len(summarized_text)}")

compare(content, summarized_text)

عند تنفيذ الكود سنحصل على تلخيص النص الأصلي content المكون من 1427 حرفًا، وكما تلاحظ يحتوي التلخيص summarized_text على 320 حرفًا فقط ويركز على العبارات والجمل الهامة في النص الأصلي.

Article text length: 1427

Trustworthy AI should enable people to decide how their data is used.<n>values and goals of a system
should be power aware and seek to minimize harm.<n>People should have agency and control over their
data and algorithmic outputs.<n>Developers need to implement strong measures to protect our data and
personal security.

Summarized length: 320

تعمل الأمور بطريقة صحيحة، لكن يبدو أن النص المُلخّص موجزًا جدًا ويحتاج إلى تضمين المزيد من التفاصيل أو السياق. لنرى فيما إذا كان بالإمكان جعل المُلخّص أكثر شمولًا وتفصيلًا. لنقم بإجراء بعض التعديلات على الكود بهدف تغيير طول ومحتوى التلخيص الناتج، بداية لنغير قيمة متغير "البذرة seed" من 248602 إلى 860912 لتوليد نتائج جديدة كما يلي:

ملاحظة: نستخدم مفهوم البذور seeds لتهيئة مولّد الأرقام العشوائية، حيث أنّه يحتاج إلى رقم يبدأ به (قيمة أولية) حتى يتمكن من إنشاء رقم عشوائي. افتراضيًّا يستخدم وقت النظام الحالي.

set_seed(860912)

# Generate the summary as tokens, with a max_new_tokens
summarized = model.generate(**batch, max_new_tokens=800)
summarized_decoded = tokenizer.batch_decode(summarized, skip_special_tokens=True)
summarized_text = summarized_decoded[0]

compare(content, summarized_text)

ستكون النتيجة على النحو التالي:

Article text length: 1427

Trustworthy AI should enable people to decide how their data is used.<n>values and goals of a system
should be power aware and seek to minimize harm.<n>People should have agency and control over their
data and algorithmic outputs.<n>Developers need to implement strong measures to protect our data and
personal security.

Summarized length: 320

يبدو أن الأمر لم ينجح أيضًا. لنحاول تجريب أسلوب فك تشفير يُسمّى "أخذ العينات sampling"، حيث يختار النموذج الكلمة التالية في النص بناءً على توزيعها الاحتمالي الشرطي (أي احتمال اختيار كلمة ما، بالنظر إلى الكلمات السابقة). هنا يكون لدينا مُتغير يسمى "درجة الحرارة temperature" للتحكم في مستوى العشوائية والإبداع في المخرجات المولّدة. هذه المعلمة تُعدّل الاحتمالات قبل أخذ العينات. تؤدي درجة الحرارة المرتفعة إلى زيادة العشوائية (أي ليس من الضروري أن يأخذ الكلمة الأكبر احتمالًا دومًا)، بالتالي مخرجات أكثر تنوعًا وإبداعًا، والعكس من ذلك يجعل عملية أخذ العينات أكثر حتمية ودقة.

set_seed(118511)
summarized = model.generate(**batch, do_sample=True, temperature=0.8, top_k=0)
summarized_decoded = tokenizer.batch_decode(summarized, skip_special_tokens=True)
summarized_text = summarized_decoded[0]
compare(content, summarized_text)

سنحصل الآن على النتيجة التالية:

Article text length: 1427

Mozilla's "Trustworthy AI" Thinking Points:.<n>People should have agency and control over their data
and algorithmic outputs.<n>Developers need to implement strong measures to protect our data.

Summarized length: 193

نلاحظ أن الخرج أقصر، لكنه ذو جودة أعلى. ربما إذا رفعنا متغير درجة الحرارة temperature، ستكون النتيجة أفضل.

set_seed(108814)
summarized = model.generate(**batch, do_sample=True, temperature=1.0, top_k=0)
summarized_decoded = tokenizer.batch_decode(summarized, skip_special_tokens=True)
summarized_text = summarized_decoded[0]
compare(content, summarized_text)

هذا هو التخليص الناتج وهو كما تلاحظ أكثر وضوحًا من التلخيص السابق:

Article text length: 1427

Mozilla's "Trustworthy AI" Thinking Points:.<n>People should have agency and control over their data
and algorithmic outputs.<n>Developers need to implement strong measures to protect our data and
personal security.<n>We need to mandate transparency so that we can fully understand these systems
and their potential for harm.

Summarized length: 325

دعونا نستخدم الآن طريقة فك تشفير تدعىtop_k`، وهي طريقة تعتمد على أخذ الكلمات k الأعلى احتمالًا (الكلمات المُرشّحة)، ثم الاختيار عشوائيًّا بينها. هذا النهج يهدف إلى السماح للنموذج بأن يكون دقيقًا مع هامش من الإبداع بفضل إدخال العشوائية في الاختيار. والمعلمة k هي معلمة يجب ضبطها بدقة لتحقيق التوازن بين الدقة والإبداع، فمع القيم الكبيرة لها قد يبدو النص بلا معنى وأقل تماسكًا، ومع القيم الصغيرة ستكون النتائج حتميّة وخاليّة من أي إبداع، وقد تتضمّن جملًا مكررة.

set_seed(226012)
summarized = model.generate(**batch, do_sample=True, top_k=50)
summarized_decoded = tokenizer.batch_decode(summarized, skip_special_tokens=True)
summarized_text = summarized_decoded[0]
compare(content, summarized_text)

بعد القيام بالتغييرات السابقة لاحظ التلخيص الناتج:

Mozilla's "Trustworthy AI" Thinking Points look at ethical issues surrounding automated decision
making.<n>values and goals of a system should be power aware and seek to minimize harm.People
should have agency and control over their data and algorithmic outputs.<n>Developers need to
implement strong measures to protect our data and personal security.

Summarized length: 355

دعونا الآن نجرّب نهجًا يدعى top-p أو أخذ عينات النواة Nucleus sampling، وهنا تشير p إلى قيمة حديّة، عند تجاوزها نتوقف عن ترشيح كلمات جديدة. بمعنى أوضح، تُفرز الكلمات تنازليًّا وفقًا لاحتمالاتها، ثم نبدأ باختيار الكلمات واحدة تلو الأخرى حتى يصبح مجموع احتمالاتها أكبر من p، ثم بعد ذلك نختار إحدى الكلمات المُرشحة عشوائيًا. توفّر هذه الطريقة أيضًا التوازن بين العشوائية والحتمية. فقيم p القريبة من 1 تعطي خرجًا أكثر تنوعًا والقيم القريبة من 0 أكثر حتمية. الفرق بينه وبين top_k هو أن عدد الكلمات المُرشّحة يكون ديناميكيًّا وأكثر قابليّة للتكيّف وفقًا للتوزيع الاحتمالي للكلمة التالية على طول مجموعة المفردات.

set_seed(21420041)
summarized = model.generate(**batch, do_sample=True, top_p=0.9, top_k=50)
summarized_decoded = tokenizer.batch_decode(summarized, skip_special_tokens=True)
summarized_text = summarized_decoded[0]
compare(content, summarized_text)

# saving this for later.
pegasus_summarized_text = summarized_text

سيكون التخليص الآن على النحو التالي:

Article text length: 1427

Mozilla's "Trustworthy AI" Thinking Points:.<n>People should have agency and control over their data
and algorithmic outputs.<n>Developers need to implement strong measures to protect our data and
personal security.<n>We need to mandate transparency so that we can fully understand these systems
and their potential for harm.

Summarized length: 325

قد نتساءل ما هي الطريقة الأفضل لفك التشفير. لسوء الحظ، لا توجد طريقة "أفضل" لفك التشفير، ويعتمد اختيار النهج الأفضل على طبيعة البيانات التي تُنشئ مُلخصًا لها ونوع هذه النصوص، وعلى درجة الإبداع و الحتمية التي تريد أن تراها في النص الناتج.

وبذلك نكون قد وصلنا إلى نهاية المقالة، حيث تحدّثنا فيها عن مهمة تلخيص النص، وعن كيفية استخدام أحدث النماذج لإنجاز ذلك.

ترجمة -وبتصرُّف- للمقال Mozilla AI Guide Launch with Summarization Code Example لصاحبه Melissa Thermidor.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...