تطبيع أعداد أزواج الكلمات
حوِّل أعداد أزواج الكلمات الخام إلى توزيعات احتمالية تجمع إلى 1.0 لكل كلمة.
- تطبيع أعداد أزواج الكلمات الخام إلى احتمالات بقسمة إجمالي التوابع
- فهم سبب الحاجة إلى التوزيعات الاحتمالية للعينة الموزونة
- معالجة الحالات القصوى: الأعداد الصفرية وكلمات التابع المفرد والمفاتيح المفقودة
- التحقق من أن الاحتمالات تجمع إلى 1.0 لكل كلمة
من الأعداد إلى الاحتمالات
تخبرك الأعداد الخام أن “the” → “cat” ظهرت 15 مرة و”the” → “dog” ظهرت 5 مرات. لكن لأخذ عيّنة من الكلمة التالية، تحتاج إلى احتمالات: يجب اختيار “cat” في 75% من الوقت و”dog” في 25%. يحوّل التطبيع الأعداد إلى توزيع تجمع فيه كل التوابع إلى 1.0.
تستخدم الخلايا أدناه الدوال load_corpus وtokenize وbuild_bigrams من الدروس 01 إلى 05. كل صفحة درس تبدأ جلسة بايثون جديدة، لذا شغّل خلية الإعداد هذه أولًا:
import csv
import string
from collections import defaultdict
with open("slm-corpus.csv", newline="") as f:
reader = csv.DictReader(f)
texts = [row["text"] for row in reader]
def load_corpus(path):
with open(path, newline="") as f:
reader = csv.DictReader(f)
return [row["text"] for row in reader]
def tokenize(text):
text = text.lower()
for char in string.punctuation:
text = text.replace(char, " ")
return text.split()
def build_bigrams(tokens):
bigrams = defaultdict(lambda: defaultdict(int))
for i in range(len(tokens) - 1):
bigrams[tokens[i]][tokens[i + 1]] += 1
return dict(bigrams)المفاهيم الأساسية
التطبيع بحلقة
لكل كلمة، اجمع أعداد توابعها، ثم اقسم كل عدد على ذلك الإجمالي:
def normalize_bigrams(bigrams):
normalized = {}
for word, followers in bigrams.items():
total = sum(followers.values())
normalized[word] = {w: c / total for w, c in followers.items()}
return normalizedالآن يُرجع normalized["the"]["cat"] عددًا عشريًا بين 0 و1 ، احتمال أن تتبع “cat” كلمةَ “the”.
مثال
raw_bigrams = {"the": {"cat": 15, "dog": 5, "bird": 10}}
norm = normalize_bigrams(raw_bigrams)
print(norm["the"])
# {'cat': 0.5, 'dog': 0.1667, 'bird': 0.3333}تجمع الاحتمالات إلى 1.0:
print(sum(norm["the"].values())) # 1.0لماذا يهم التطبيع في أخذ العينات
يحتاج random.choices() إلى أوزان تمثل الاحتمال النسبي. إذا مررت الأعداد الخام (15، 5، 10)، فسيعمل ، لكن امتلاك احتمالات صحيحة (0.5، 0.167، 0.333) يجعل النموذج قابلاً للنقل وقابلاً للمقارنة عبر أحجام متن مختلفة.
import random
followers = list(norm["the"].keys())
weights = list(norm["the"].values())
next_word = random.choices(followers, weights=weights, k=1)[0]
print(f"Next word: {next_word}")معالجة الحالات القصوى
بعض الكلمات ليس لها توابع (آخر كلمة في المتن، أو كلمات تظهر فقط في نهاية جمل). لن يحتوي جدول أزواج الكلمات على إدخالات لها:
def normalize_bigrams(bigrams):
normalized = {}
for word, followers in bigrams.items():
if not followers:
continue # skip words with no followers
total = sum(followers.values())
normalized[word] = {w: c / total for w, c in followers.items()}
return normalizedيمنع تخطي الإدخالات الفارغة أخطاء القسمة على صفر.
مسار كامل
إليك كيف يتناسب التطبيع مع المسار الكامل:
texts = load_corpus("slm-corpus.csv")
tokens = tokenize(" ".join(texts))
bigrams = build_bigrams(tokens)
model = normalize_bigrams(bigrams)
# Check a sample
print(f"Words in model: {len(model)}")
print(f"Followers of 'the': {list(model.get('the', {}).keys())[:5]}")حفظ النموذج
قد ترغب في حفظ جدول أزواج الكلمات المطبَّع لإعادة الاستخدام. وبما أنه قاموس متداخل من الأعداد العشرية، يعمل json بشكل جيد:
import json
with open("bigram_model.json", "w") as f:
json.dump(model, f)
# Reload later
with open("bigram_model.json") as f:
model = json.load(f)جرّب بنفسك
ابنِ جدول أزواج الكلمات وطبّعه، ثم تحقق:
- هل تجمع احتمالات “the” إلى 1.0؟
- كم عدد الكلمات التي ليس لها توابع (عدّتها صفر)؟
- ما الكلمة الأرجح أن تتبع “the”؟
model = normalize_bigrams(bigrams)
the_followers = model.get("the", {})
top_follower = max(the_followers, key=the_followers.get)
print(f"Most likely after 'the': '{top_follower}' ({the_followers[top_follower]:.3f})")الخلاصات الرئيسية
- التطبيع يحوّل الأعداد الخام إلى احتمالات تجمع إلى 1.0 لكل كلمة
- يستخدم
random.choices()هذه الاحتمالات كأوزان لأخذ العينات الموزون - تخطَّ الكلمات التي ليس لها توابع لتجنب القسمة على صفر
- احفظ النماذج المطبَّعة باستخدام
json.dump()لإعادة استخدامها عبر النصوص البرمجية
تحدي الممارسة
اكتب دالة bigram_stats(model) تطبع لكل كلمة: الكلمة وعدد التوابع والكلمة التالية الأرجح. حدّد المخرج بأعلى 10 كلمات بإجمالي عدد التوابع.
def bigram_stats(model, top_n=10):
words = sorted(model, key=lambda w: sum(model[w].values()), reverse=True)
for word in words[:top_n]:
followers = model[word]
total = sum(followers.values())
best = max(followers, key=followers.get)
print(f"'{word}': {len(followers)} followers, best=''{best}'' ({followers[best]:.3f})")1. لماذا نطبع أعداد أزواج الكلمات؟
2. ما هي P(word2 | word1) لزوج كلمات؟
3. إذا ظهرت the 1000 مرة وظهر (the, cat) 50 مرة، فما قيمة P(cat | the)؟