معاينة مختبر آمنة
Embeddings TF
هذي معاينة منقّحة للقراءة فقط؛ ما فيه أي شيء يشتغل داخل الصفحة.
قراءة فقط
معاينة الدفتر
Embeddings TF
> **ملاحظة بيئة التشغيل المدمجة:** هالمعاينة تستخدم عيّنة أخبار صغيرة وثابتة وآمنة من ناحية الحقوق عشان تكون النتايج قابلة للتكرار من غير شبكة. النتايج بالحجم الكامل تحتاج مجموعة البيانات الموثّقة بالدرس داخل بيئة خارجية معتمدة.
## التضمينات
في المثال السابق، اشتغلنا على متجهات **نموذج حقيبة الكلمات** عالية الأبعاد طولها `vocab_size`، وحوّلنا بشكل صريح متجهات التمثيل الموضعي منخفضة الأبعاد إلى تمثيل one-hot متناثر. لكن تمثيل one-hot ما يستخدم الذاكرة بكفاءة. وفوق هذا، يتعامل مع كل كلمة بمعزل عن غيرها؛ لذلك متجهاته ما تعبّر عن التشابه الدلالي بين الكلمات.
في هالوحدة بنكمل استكشاف مجموعة البيانات **News AG**. بالبداية بنحمّل البيانات ونجيب بعض التعريفات من الوحدة السابقة.
import tensorflow as tf
from tensorflow import keras
import numpy as np
keras.utils.set_random_seed(2026)
_course_titles = tf.constant([
"Regional leaders agree on a public health plan",
"The home team wins the basketball final",
"Markets rise after a strong business report",
"Researchers release an efficient intelligence model",
"Diplomats begin a new round of peace talks",
"The tennis champion returns after an injury",
"Retail sales improve with consumer confidence",
"A software update improves privacy",
"Observers publish guidance for a national election",
"A football club signs a young goalkeeper",
"A local company creates new apprenticeships",
"Engineers test a compact satellite sensor",
"Countries publish a shared emergency schedule",
"Swimmers prepare for the regional games",
"The central bank reviews recent economic data",
"Scientists document a lower-energy battery process",
])
_course_descriptions = tf.constant([
"World policy and international cooperation",
"Sports results and player development",
"Business revenue and economic indicators",
"Science and technology research",
"World diplomacy and humanitarian aid",
"Sports training and competition",
"Business investment and employment",
"Science security and technology",
"World elections and public institutions",
"Sports teams and competition",
"Business jobs and local investment",
"Science engineering and space technology",
"World cooperation and emergency planning",
"Sports preparation and inclusive events",
"Business banking and economic policy",
"Science energy and materials research",
])
_course_labels = tf.constant(
[0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3],
dtype=tf.int64,
)
_course_records = {
"title": _course_titles,
"description": _course_descriptions,
"label": _course_labels,
}
ds_train = tf.data.Dataset.from_tensor_slices(_course_records).take(12)
ds_test = tf.data.Dataset.from_tensor_slices(_course_records).skip(12)### وش هو التضمين؟
فكرة **التضمين** إننا نمثّل الكلمات بمتجهات كثيفة وأقل أبعادًا تعكس المعنى الدلالي للكلمة. قدّام بنتكلم عن طريقة بناء تضمينات كلمات لها معنى، أما الحين فخلونا نفهم التضمين على إنه طريقة لتقليل أبعاد متجه الكلمة.
تأخذ طبقة التضمين كلمة كمدخل وتطلع متجهًا بالحجم المحدد في `embedding_size`. وهي تشبه طبقة `Dense` من ناحية، لكن بدل ما تستقبل متجه one-hot، تقدر تستقبل رقم الكلمة.
إذا استخدمنا طبقة التضمين كأول طبقة في الشبكة، نقدر ننتقل من نموذج حقيبة الكلمات إلى نموذج **حقيبة التضمينات**. أول شيء نحوّل كل كلمة في النص إلى تضمينها، ثم نحسب دالة تجميع على كل هالتضمينات، مثل `sum` أو `average` أو `max`.
> **وصف الشكل:** رسم يوضّح مصنّف تضمين لتسلسل من خمس كلمات.
**الشبكة العصبية** المستخدمة في التصنيف عندنا تتكوّن من هالطبقات:
* طبقة `TextVectorization`: تستقبل سلسلة نصية وتطلع موترًا من أرقام الرموز. بنحدّد حجمًا مناسبًا للمفردات في `vocab_size`، ونتجاهل الكلمات الأقل تكرارًا. شكل الإدخال 1، وشكل الإخراج $n$؛ لأن الناتج $n$ رمز، وكل رمز يحمل رقمًا من 0 إلى `vocab_size`.
* طبقة `Embedding`: تستقبل $n$ أرقام وتحوّل كل رقم إلى متجه كثيف بطول محدد، وهو 100 في مثالنا. وبكذا يتحوّل موتر الإدخال ذي الشكل $n$ إلى موتر شكله $n\times 100$.
* طبقة تجميع: تأخذ متوسط هالموتر على المحور الأول؛ يعني تحسب متوسط موترات الإدخال الـ$n$ المقابلة للكلمات المختلفة. بننفّذها بطبقة `Lambda` ونمرّر لها دالة حساب المتوسط. شكل الإخراج 100، ويكون هو التمثيل الرقمي لتسلسل الإدخال كاملًا.
* المصنّف الخطي الأخير `Dense`.
vocab_size = 30000
batch_size = 128
vectorizer = keras.layers.TextVectorization(max_tokens=vocab_size, output_mode="int")
model = keras.models.Sequential([
keras.Input(shape=(), dtype=tf.string),
vectorizer,
keras.layers.Embedding(vocab_size, 100, mask_zero=True),
keras.layers.GlobalAveragePooling1D(),
keras.layers.Dense(4, activation="softmax"),
])
model.summary()في ناتج `summary` وتحديدًا عمود **شكل الإخراج**، البعد الأول للموتر `None` هو حجم الدفعة المصغّرة، والثاني هو طول تسلسل الرموز. أطوال تسلسلات الرموز تختلف داخل الدفعة، وبنشوف كيف نتعامل معها في القسم الجاي.
الحين خلونا ندرّب الشبكة:
def extract_text(x):
return x['title']+' '+x['description']
def tupelize(x):
return (extract_text(x),x['label'])
print("Training vectorizer")
vectorizer.adapt(ds_train.take(500).map(extract_text))
model.compile(loss='sparse_categorical_crossentropy',metrics=['acc'])
model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))> **ملاحظة:** نبني vectorizer اعتمادًا على جزء من البيانات عشان نسرّع العملية. ممكن بسبب كذا ما تدخل كل رموز النص في المفردات؛ وقتها تُتجاهل هالرموز وقد تنخفض الدقة شوي. لكن في التطبيقات الفعلية، غالبًا تعطي عينة من النص تقديرًا زينًا للمفردات.
### التعامل مع اختلاف طول التسلسلات
خلونا نفهم كيف يصير التدريب داخل الدفعات المصغّرة. في المثال اللي فوق، شكل موتر الإدخال 1، ونستخدم دفعات حجمها 128، لذلك يكون حجمه الفعلي $128 \times 1$. لكن عدد الرموز يختلف من جملة للثانية. وإذا طبّقنا طبقة `TextVectorization` على إدخال واحد، يختلف عدد الرموز الناتج بحسب طريقة تجزئة النص إلى رموز:
print(vectorizer('Hello, world!'))
print(vectorizer('I am glad to meet you!'))لكن لما نطبّق vectorizer على عدة تسلسلات، لازم يطلع موترًا مستطيل الشكل؛ لذلك يعبّي العناصر غير المستخدمة برمز PAD، وهو صفر في حالتنا:
vectorizer(['Hello, world!','I am glad to meet you!'])وهنا نقدر نشوف التضمينات:
model.layers[1](vectorizer(['Hello, world!','I am glad to meet you!'])).numpy()> **ملاحظة:** لتقليل الحشو، يفيد أحيانًا إننا نرتّب كل تسلسلات مجموعة البيانات من الأقصر للأطول، أو بدقة أكثر بحسب عدد الرموز. كذا تحتوي كل دفعة مصغّرة على تسلسلات أطوالها متقاربة.
## فحص تضمين دلالي صغير
قدّم Word2Vec طريقتين مؤثرتين لتعلّم متجهات الكلمات من السياق المحلي. نموذج حقيبة الكلمات المستمر (CBoW) يتنبأ بالكلمة الوسطى من الكلمات اللي حولها، بينما skip-gram يتنبأ بالكلمات المحيطة انطلاقًا من كلمة الوسط. التضمينات الإنتاجية عادةً تتدرّب على متون ضخمة جدًا.
عشان يكون التمرين واضحًا وقابلًا للتكرار، تستخدم هالنسخة فضاء متجهات صغيرًا مكتوبًا خصيصًا للدرس بدل تنزيل نموذج متغيّر حجمه عدة جيجابايت. كل واحد من إحداثياته الثمانية يمثّل مجموعة مفاهيم ظاهرة مثل الملكية، والرياضة، والأعمال، والتقنية. هالمتجهات مب نموذجًا مدرّبًا مسبقًا، ولا تصلح لقياس المعاني الواقعية؛ هدفها إننا نفحص التشابه، وحساب علاقات الكلمات، وتهيئة طبقة Keras من غير ما نخفي البيانات أو نحتاج تنزيلًا من الشبكة.
# course-edition deterministic TensorFlow semantic vectors v1
COURSE_SEMANTIC_GROUPS = [
("royalty", {"king", "queen"}),
("masculine", {"king", "man"}),
("feminine", {"queen", "woman"}),
("sports", {"athletes", "basketball", "coach", "football", "play", "runner", "swimming", "team", "tennis", "volleyball"}),
("business", {"bank", "business", "companies", "contracts", "funds", "market", "payment", "prices", "revenue", "sales"}),
("technology", {"battery", "cloud", "engineers", "model", "neural", "researchers", "robot", "satellite", "scientists", "sensor", "software", "technology"}),
("world", {"council", "countries", "diplomats", "election", "ministers", "paris", "regional", "summit"}),
("care", {"emergency", "health", "hospital", "humanitarian", "medicine", "relief", "safety"}),
]
COURSE_SEMANTIC_TOKENS = sorted({
token
for _name, members in COURSE_SEMANTIC_GROUPS
for token in members
})
def course_semantic_vector(word):
"""Return the eight visible concept coordinates for one token."""
return [
1.0 if word in members else 0.0
for _name, members in COURSE_SEMANTIC_GROUPS
]
def course_cosine_similarity(left, right):
numerator = sum(a * b for a, b in zip(left, right))
left_norm = sum(value * value for value in left) ** 0.5
right_norm = sum(value * value for value in right) ** 0.5
if left_norm == 0.0 or right_norm == 0.0:
return -1.0
return numerator / (left_norm * right_norm)
class CourseSemanticVectors:
"""Small edition-authored vectors with a familiar lookup interface."""
def __init__(self):
self.tokens = list(COURSE_SEMANTIC_TOKENS)
self.vectors = {
token: course_semantic_vector(token)
for token in self.tokens
}
def get_vector(self, word):
if word not in self.vectors:
raise ValueError(f"No authored semantic vector for {word!r}")
return list(self.vectors[word])
def nearest_to_vector(self, query, exclude=(), topn=5):
excluded = set(exclude)
ranked = [
(token, course_cosine_similarity(query, vector))
for token, vector in self.vectors.items()
if token not in excluded
]
ranked.sort(key=lambda item: (-item[1], item[0]))
return ranked[:topn]
def most_similar(self, word=None, positive=None, negative=None, topn=5):
positive_words = list(positive or ([] if word is None else [word]))
negative_words = list(negative or [])
if not positive_words and not negative_words:
raise ValueError("Provide a word or at least one positive/negative token")
query = [0.0] * len(COURSE_SEMANTIC_GROUPS)
for token in positive_words:
query = [
current + value
for current, value in zip(query, self.get_vector(token))
]
for token in negative_words:
query = [
current - value
for current, value in zip(query, self.get_vector(token))
]
return self.nearest_to_vector(
query,
exclude={*positive_words, *negative_words},
topn=topn,
)
course_semantics = CourseSemanticVectors()for word, score in course_semantics.most_similar("neural"):
print(f"{word} -> {score:.3f}")كل كلمة ترتبط بصف واحد في مصفوفة المتجهات المكتوبة للدرس. هنا كل إحداثيات كلمة `play`:
course_semantics.get_vector("play")يسهل فحص حساب المتجهات لما يكون معنى كل إحداثي واضحًا. المتجهات المكتوبة للدرس تخلي `king - man + woman` توصل بالضبط إلى `queen`:
course_semantics.most_similar(
positive=["king", "woman"],
negative=["man"],
topn=1,
)[0]الدالة المساعدة تستخدم جمع المتجهات وطرحها، وتشابه جيب التمام، وكسر التعادل بطريقة حتمية. نقدر نكتب العلاقة نفسها مباشرة:
qvec = [
king - man + woman
for king, man, woman in zip(
course_semantics.get_vector("king"),
course_semantics.get_vector("man"),
course_semantics.get_vector("woman"),
)
]
course_semantics.nearest_to_vector(
qvec,
exclude={"king", "man", "woman"},
topn=1,
)[0]ما نحتاج معاملات مخفية أو `argmin` على مصفوفة خارجية هنا. الإحداثيات الظاهرة تعطي العلاقة مباشرة، ودالة البحث ترتّب تشابه جيب التمام ثم تكسر أي تعادل أبجديًا عشان تتكرر النتيجة نفسها.
يتعلّم CBoW وskip-gram تضمينات تنبؤية من السياقات المحلية. يضيف FastText بنية أجزاء الكلمة، بينما يبدأ GloVe من أعداد التشارك في المتن كاملًا. الفضاء المكتوب لهالنسخة أبسط عن قصد: يوضح الآلية قبل درس لاحق يدرّب التضمينات من البيانات.
## تهيئة طبقة تضمين في Keras
أول شيء نطابق مفردات أداة تحويل النص المتعلّمة مع الإحداثيات الدلالية المكتوبة للدرس. كل رمز يأخذ صفًا حتميًا؛ والرموز اللي ما تدخل ضمن مجموعات المفاهيم الظاهرة تأخذ متجهًا صفريًا، وبكذا تكون حدود المثال واضحة بدل تعبئة الكلمات الناقصة عشوائيًا.
vocab = vectorizer.get_vocabulary()
W = np.asarray(
[course_semantic_vector(word) for word in vocab],
dtype="float32",
)
vocab_size = len(vocab)
embed_size = W.shape[1]
covered = sum(any(row) for row in W)
print(f"Embedding size: {embed_size}; authored coverage: {covered}/{vocab_size}")نقدر نستخدم المصفوفة الناتجة مباشرة لتهيئة طبقة `Embedding` مجمّدة في Keras.
emb = keras.layers.Embedding(
input_dim=vocab_size,
output_dim=embed_size,
weights=[W],
trainable=False,
mask_zero=True,
)
model = keras.models.Sequential([
keras.Input(shape=(), dtype=tf.string),
vectorizer,
emb,
keras.layers.GlobalAveragePooling1D(),
keras.layers.Dense(4, activation="softmax"),
])الحين ندرّب المصنّف عشان نجرب المسار كاملًا من تحويل النص إلى التضمين:
model.compile(loss="sparse_categorical_crossentropy", metrics=["acc"])
model.fit(
ds_train.map(tupelize).batch(batch_size),
validation_data=ds_test.map(tupelize).batch(batch_size),
epochs=2,
)### استخدام مفردات تضمين محددة
تقدر `keras.layers.TextVectorization` الحالية تستقبل مفردات مراجعة بشكل مباشر. كذا نضمن إن أداة التحويل ومصفوفة التضمين تستخدمان ترتيب الصفوف نفسه، بما فيه رمزا الحشو والكلمة المجهولة المحجوزان في Keras.
semantic_vectorizer = keras.layers.TextVectorization(output_mode="int")
semantic_vectorizer.set_vocabulary(COURSE_SEMANTIC_TOKENS)
semantic_vocabulary = semantic_vectorizer.get_vocabulary()
semantic_W = np.asarray(
[course_semantic_vector(word) for word in semantic_vocabulary],
dtype="float32",
)نبني طبقة Keras المطابقة من المفردات نفسها، وبعدها ندرّب المصنّف الثاني:
semantic_embedding = keras.layers.Embedding(
input_dim=len(semantic_vocabulary),
output_dim=semantic_W.shape[1],
weights=[semantic_W],
trainable=False,
mask_zero=True,
)
semantic_model = keras.models.Sequential([
keras.Input(shape=(), dtype=tf.string),
semantic_vectorizer,
semantic_embedding,
keras.layers.GlobalAveragePooling1D(),
keras.layers.Dense(4, activation="softmax"),
])
semantic_model.compile(
loss="sparse_categorical_crossentropy",
metrics=["acc"],
)
semantic_model.fit(
ds_train.map(tupelize).batch(batch_size),
validation_data=ds_test.map(tupelize).batch(batch_size),
epochs=2,
)الإحداثيات المكتوبة للدرس صغيرة وناقصة عن قصد. هي توضّح مطابقة المفردات ونقل الأوزان؛ ودرجة المصنّف مب دليل على تغطية دلالية تصلح للإنتاج. أي تضمين مدرّب مسبقًا يحتاج نموذجًا مراجعًا بشكل منفصل، وترخيصًا واضحًا، ومراجعة ثابتة، وميزانية تشغيل مناسبة.
## التضمينات السياقية
من أهم قيود تمثيلات التضمين التقليدية المدرّبة مسبقًا، مثل Word2Vec، إنها مع قدرتها على التقاط جزء من معنى الكلمة ما تقدر تميّز بين معانيها المختلفة. وهذا ممكن يسبب مشكلة للنماذج اللي تجي بعدها.
مثلًا، كلمة 'play' لها معنيان مختلفان بهالجملتين:
- I went to a **play** at the theater.
- John wants to **play** with his friends.
التضمينات المدرّبة مسبقًا تمثّل المعنيين لكلمة 'play' بالتضمين نفسه. عشان نتجاوز هالقيد، نحتاج نبني التضمينات بالاعتماد على **نموذج اللغة**؛ وهو نموذج يتدرّب على متن نصي كبير و*يعرف* كيف تجتمع الكلمات في سياقات مختلفة. شرح التضمينات السياقية خارج نطاق هالدرس، لكن بنرجع لها لما نتكلم عن نماذج اللغة في الوحدة الجاية.
حذفنا المخرجات وعدّادات التشغيل والودجات والمحتوى النشط وقت الاستيراد. شغّل الدفاتر بس في بيئة خارجية تثق فيها.
سجّل تطبيقك
التسجيل اختياري، يفيدك تتذكر وش طبّقت، ولا يمنع إكمال الدورة.