يا جماعة الخير، السلام عليكم ورحمة الله.
خلوني أحكيلكم قصة صارت معي قبل كم سنة. كنا شغالين على مشروع كبير لأرشفة آلاف الوثائق والمقالات لعميل مهم. جزء أساسي من المشروع كان بناء محرك بحث داخلي يساعد المستخدمين يلاقوا اللي بدهم إياه بسرعة. في البداية، عملنا إشي بسيط ومباشر: أي كلمة ببحث عنها المستخدم، بنروح بنعدّها في كل وثيقة، والوثيقة اللي فيها الكلمة مكررة أكثر، بتطلع أول إشي. إشي بسيط ومنطقي، صح؟
غلط. غلط كبير.
بعد إطلاق النسخة الأولية، بلشت الشكاوي تيجي. اتصل عليّ مدير المشروع عند العميل، زلمة ختيار وفاهم شغله، وقلي بنبرة حادة: “يا أبو عمر، شو هالشغل؟ عم ببحث عن ‘قانون حماية الملكية الفكرية’، بيطلعلي كل وثيقة فيها كلمة ‘قانون’ بالأول! بدي قانون الملكية الفكرية تحديداً، مش أي قانون والسلام!”.
وقتها حسيت حالي انحطيت في موقف صعب. البحث تبعنا كان “أعمى”. كان بعامل كل الكلمات بنفس الأهمية. كلمة “قانون” وكلمة “الملكية” وكلمة “الفكرية” الهم نفس الوزن عنده، مع إنه منطقياً، كلمة “الملكية” و “الفكرية” هم اللي بميزوا هاي الوثيقة عن غيرها. قعدت مع حالي، شربت فنجان قهوة ثقيل، وقلت لحالي: “يا أبو عمر، لازم يكون في حل أذكى من هيك”. وهون بدأت رحلتي مع خوارزمية بسيطة لكن عبقرية، غيرت نظرتنا للبحث النصي تماماً: خوارزمية TF-IDF.
المشكلة: لماذا كان بحثنا “أعمى”؟
المشكلة اللي واجهناها، واللي بتواجه أي نظام بحث بسيط، هي “تساوي قيمة الكلمات”. النظام الأولي تبعنا كان مبني على فكرة بسيطة اسمها “Bag of Words” أو “كيس الكلمات”، اللي هي بس بتعد تكرار الكلمات بدون فهم للسياق أو لأهمية الكلمة.
هذا الأسلوب أدى لمشكلتين رئيسيتين:
- هيمنة الكلمات الشائعة (Stop Words): كلمات مثل “في”، “من”، “إلى”، “هو”، “هي”، “كان”، موجودة بكثرة في كل النصوص العربية. نظامنا كان يعطيها وزن كبير لأنها بتتكرر كثير، وبالتالي كانت النتائج تطلع غير دقيقة بالمرة.
- ضياع الكلمات المفتاحية النادرة: في مثال العميل، كلمة “قانون” شائعة جداً في مجموعة الوثائق القانونية، بينما كلمتي “الملكية” و”الفكرية” أقل شيوعاً، وبالتالي أكثر تحديداً وتمييزاً للموضوع. البحث الأعمى تبعنا كان يغرق هاي الكلمات المهمة في بحر من الكلمات العامة.
كنا بحاجة لطريقة تخلي الخوارزمية تفهم إنه مش كل الكلمات انخلقت متساوية. كنا بحاجة لطريقة تقدر تميز الكلمة المهمة من الكلمة العادية. وهنا يأتي دور الـ TF-IDF.
الحل السحري: تفكيك خوارزمية TF-IDF
اسم الخوارزمية “TF-IDF” ممكن يخوّف شوي، لكنها في الحقيقة بسيطة جداً ومكونة من جزأين، كل جزء بجاوب على سؤال مهم.
TF-IDF هي اختصار لـ Term Frequency – Inverse Document Frequency. خلينا نفصصها وحدة وحدة.
الجزء الأول: تكرار المصطلح (Term Frequency – TF)
هذا الجزء بسيط ومباشر، وبجاوب على سؤال: “ما مدى تكرار كلمة معينة داخل المستند الواحد؟”
المنطق بقول إنه إذا كلمة تكررت كثير في مقالة معينة، فهي على الأغلب كلمة مهمة بالنسبة لهي المقالة بالذات. مثلاً، لو عنا مقالة عن “الذكاء الاصطناعي”، طبيعي نلاقي كلمة “الذكاء” و”الاصطناعي” مكررة فيها أكثر من مرة.
أبسط طريقة لحساب الـ TF هي:
TF(كلمة, مستند) = (عدد مرات ظهور الكلمة في المستند) / (إجمالي عدد الكلمات في المستند)
هيك بنكون عملنا “تطبيع” (Normalization) للقيمة، عشان ما تتأثر بحجم المستند. المستندات الطويلة طبيعي يكون فيها تكرار أعلى.
الجزء الثاني: مقلوب تردد المستند (Inverse Document Frequency – IDF)
وهذا هو الجزء العبقري اللي حل مشكلتنا. الـ IDF بجاوب على سؤال مختلف تماماً: “ما مدى ندرة هذه الكلمة في كل المستندات الموجودة عندي؟”
الفكرة هون هي إعطاء وزن أكبر للكلمات النادرة، وتقليل وزن الكلمات الشائعة جداً (زي “في” و “على” و “كان”). إذا كلمة ظهرت في كل المستندات تقريباً، فهي على الأغلب كلمة عامة وغير مهمة للتمييز بين المستندات. أما إذا ظهرت في عدد قليل من المستندات، فهذا يعني أنها كلمة متخصصة ومهمة.
طريقة حسابه كالتالي:
IDF(كلمة) = لوغاريتم (إجمالي عدد المستندات) / (عدد المستندات التي تحتوي على الكلمة + 1)
بنستخدم اللوغاريتم عشان “نهدّي” القيم شوي وما تطلع أرقام فلكية، وبنضيف “+1” في المقام عشان نتجنب القسمة على صفر لو الكلمة مش موجودة أبداً.
لاحظ الفرق: الـ TF بيهتم بالمستند الواحد، أما الـ IDF بيهتم بكل “الخزان” أو “الكوربس” (Corpus) تبع المستندات.
الضربة القاضية: حساب نتيجة TF-IDF النهائية
الآن، عشان نحصل على الوزن النهائي لكل كلمة في كل مستند، بنضرب القيمتين ببعض بكل بساطة:
TF-IDF(كلمة, مستند) = TF(كلمة, مستند) * IDF(كلمة)
النتيجة؟ الكلمات اللي بتحصل على أعلى قيمة TF-IDF هي الكلمات اللي:
- تتكرر بشكل معقول داخل مستند واحد (TF مرتفع).
- ونادرة نسبياً على مستوى كل المستندات (IDF مرتفع).
وهذا بالضبط ما كنا نحتاجه! كلمة “قانون” في مثال العميل كان الـ IDF تبعها منخفض جداً لأنها موجودة في كل مكان، بينما كلمتي “الملكية” و “الفكرية” كان الـ IDF تبعهم أعلى بكثير، ولما انضربوا بالـ TF تبعهم، أعطوا وزن نهائي أعلى بكثير، وصارت الوثائق الصحيحة تظهر في مقدمة نتائج البحث.
من النظرية إلى التطبيق: مثال بالكود
الكلام النظري حلو، بس إحنا المبرمجين بنحب نشوف الكود. “ورجيني الكود”، زي ما بنحكي. لحسن الحظ، تطبيق TF-IDF اليوم سهل جداً بفضل المكتبات الجاهزة مثل scikit-learn في بايثون.
لنفترض عنا مجموعة المستندات التالية:
# documents_arabic.py
corpus = [
"الذكاء الاصطناعي هو مستقبل التكنولوجيا",
"يعتبر تعلم الآلة فرع من فروع الذكاء الاصطناعي",
"مستقبل فلسطين يعتمد على التكنولوجيا والتعليم",
]
وهاي طريقة حساب مصفوفة TF-IDF باستخدام بايثون:
from sklearn.feature_extraction.text import TfidfVectorizer
import pandas as pd
# نفس مجموعة المستندات من المثال السابق
corpus = [
"الذكاء الاصطناعي هو مستقبل التكنولوجيا",
"يعتبر تعلم الآلة فرع من فروع الذكاء الاصطناعي",
"مستقبل فلسطين يعتمد على التكنولوجيا والتعليم",
]
# 1. تهيئة الـ Vectorizer
# هذا الكائن سيقوم بكل العمل الشاق: التقطيع، العد، وحساب TF-IDF
vectorizer = TfidfVectorizer()
# 2. تدريب النموذج وحساب المصفوفة
# .fit_transform بتعمل شغلتين: بتتعلم كل الكلمات (المفردات) في الكوربس
# وبتحول كل مستند إلى متجه (vector) من أرقام TF-IDF
tfidf_matrix = vectorizer.fit_transform(corpus)
# 3. عرض النتائج بشكل مفهوم
# لنرى الكلمات التي تعلمها النموذج
feature_names = vectorizer.get_feature_names_out()
# لنحول المصفوفة المتفرقة (sparse matrix) إلى مصفوفة عادية ونعرضها
# باستخدام مكتبة Pandas لتسهيل القراءة
df = pd.DataFrame(tfidf_matrix.toarray(), columns=feature_names)
print(df)
النتيجة اللي رح تطلعلك رح تكون جدول، كل صف يمثل مستند، وكل عمود يمثل كلمة، والقيم اللي جوا هي أوزان TF-IDF المحسوبة. رح تلاحظ إنه كلمة زي “الاصطناعي” الها وزن عالي في المستند الأول والثاني، لكن وزنها صفر في المستند الثالث. وكلمة زي “فلسطين” الها وزن عالي جداً في المستند الثالث فقط، لأنها نادرة ومهمة في سياقها.
نصائح أبو عمر العملية 📝
بعد سنين من الشغل مع هاي الخوارزمية وغيرها، جمعت شوية نصائح من أرض الواقع بتمنى تفيدكم:
- التنظيف أولاً (Preprocessing is King): قبل ما ترمي النص على الخوارزمية، لازم تنظفه. هاي أهم خطوة. اعمل “تطبيع” (Normalization) للحروف (مثلاً توحيد الـ “أ” والـ “إ” والـ “آ” إلى “ا”). احذف علامات الترقيم والأرقام إذا ما كانت مهمة. هاي الخطوة لحالها بترفع دقة النتائج 50% على الأقل.
- التعامل الذكي مع الكلمات المتوقفة (Stop Words): معظم المكتبات بتيجي مع قائمة جاهزة للـ Stop Words (زي “من”, “إلى”, “على”). استخدامها فكرة جيدة كبداية، لكن كن حذراً. أحياناً، كلمة تعتبر Stop Word في سياق عام، بتكون مهمة في سياق متخصص. دائماً راجع القائمة وعدّلها حسب مشروعك.
- استخدم التجذيع أو التصريف (Stemming/Lemmatization): الكلمات “يعلم”، “تعليم”، “معلم”، “علوم” كلها من نفس الجذر “علم”. استخدام تقنيات التجذيع (Stemming) أو التصريف (Lemmatization) لتوحيد هاي الكلمات تحت جذر واحد ممكن يحسن النتائج بشكل كبير، خصوصاً في اللغة العربية الغنية بالاشتقاقات.
- اعرف حدود الـ TF-IDF: هاي الخوارزمية عظيمة، لكنها مش الحل لكل إشي. هي لا تفهم معنى الكلمات أو السياق (مثلاً، ما بتعرف إنه “سيارة” و”مركبة” الهم نفس المعنى تقريباً). ولا بتهتم لترتيب الكلمات. لما توصل لمرحلة تحتاج فيها فهم أعمق للمعنى، وقتها لازم تبلش تطلع على تقنيات أحدث مثل Word Embeddings (مثل Word2Vec) ونماذج Transformers (مثل BERT).
الخلاصة: من الظلام إلى النور
خوارزمية TF-IDF كانت بالنسبة إلنا نقلة نوعية. أخذت محرك البحث تبعنا من نظام “أعمى” بيرجع نتائج عشوائية وغير مفيدة، إلى نظام “ذكي” قادر يميز بين الغث والسمين، ويعرف شو هي الكلمات المهمة فعلاً في كل مستند. هي مش مجرد معادلة رياضية، بل هي طريقة تفكير في تقييم أهمية المعلومات.
نصيحتي الأخيرة لكل مطور أو مهتم بهذا المجال: لا تستهينوا بالأساسيات. خوارزمية مثل TF-IDF، عمرها عقود، لكنها لا تزال أداة قوية جداً ومفيدة في عدد لا يحصى من التطبيقات، من محركات البحث وأنظمة التوصية إلى تصنيف النصوص وتحليل المشاعر. افهمها جيداً، طبقها صح، ورح تفتحلك أبواب كثيرة في عالم معالجة اللغات الطبيعية والذكاء الاصطناعي. بالتوفيق يا جماعة! 👍