![]() | ![]() | ![]() | ![]() |
لا تعد word2vec خوارزمية مفردة ، بل هي عبارة عن مجموعة من البنى والتحسينات النموذجية التي يمكن استخدامها لتعلم تضمين الكلمات من مجموعات البيانات الكبيرة. لقد أثبتت عمليات التضمين التي تم تعلمها من خلال word2vec نجاحها في مجموعة متنوعة من مهام معالجة اللغة الطبيعية.
اقترحت هذه الأوراق طريقتين لتعلم تمثيل الكلمات:
- نموذج كيس الكلمات المستمر : يتنبأ بالكلمة الوسطى بناءً على كلمات السياق المحيطة. يتكون السياق من بضع كلمات قبل وبعد الكلمة (الوسطى) الحالية. تسمى هذه البنية بنموذج حقيبة الكلمات لأن ترتيب الكلمات في السياق ليس مهمًا.
- نموذج التخطي المستمر : يتنبأ بالكلمات ضمن نطاق معين قبل الكلمة الحالية وبعدها في نفس الجملة. ويرد أدناه مثال عملي لهذا.
ستستخدم أسلوب تخطي الجرام في هذا البرنامج التعليمي. أولاً ، سوف تستكشف التخطي والمفاهيم الأخرى باستخدام جملة واحدة للتوضيح. بعد ذلك ، ستقوم بتدريب نموذج word2vec الخاص بك على مجموعة بيانات صغيرة. يحتوي هذا البرنامج التعليمي أيضًا على رمز لتصدير حفلات الزفاف المدربة وتصورها في TensorFlow Embedding Projector .
تخطي الجرام وأخذ العينات السلبية
بينما يتنبأ نموذج كيس الكلمات بكلمة في ضوء السياق المجاور ، يتنبأ نموذج تخطي الجرام بالسياق (أو الجيران) للكلمة ، بالنظر إلى الكلمة نفسها. تم تدريب النموذج على تخطي الجرامات ، وهي n-grams تسمح بتخطي الرموز (انظر الرسم البياني أدناه للحصول على مثال). يمكن تمثيل سياق الكلمة من خلال مجموعة من أزواج skip-gram من (target_word, context_word)
حيث يظهر context_word
في السياق المجاور لـ target_word
.
تأمل الجملة التالية المكونة من ثماني كلمات:
كان الطريق الواسع يتلألأ في الشمس الحارقة.
يتم تحديد كلمات السياق لكل كلمة من الكلمات الثمانية في هذه الجملة بحجم النافذة. يحدد حجم النافذة مدى الكلمات على جانبي target_word
والتي يمكن اعتبارها context word
. يوجد أدناه جدول تخطي جرامات الكلمات المستهدفة بناءً على أحجام النوافذ المختلفة.
الهدف التدريبي لنموذج تخطي الجرام هو تعظيم احتمالية توقع كلمات السياق بالنظر إلى الكلمة المستهدفة. بالنسبة لسلسلة من الكلمات w 1 ، w 2 ، ... w T ، يمكن كتابة الهدف على أنه متوسط احتمالية السجل
حيث c
هو حجم سياق التدريب. تحدد الصيغة الأساسية للتخطي هذا الاحتمال باستخدام دالة softmax.
حيث v و v ' هما الهدف وتمثيل متجه السياق للكلمات و W هو حجم المفردات.
يتضمن حساب مقام هذه الصيغة إجراء softmax كامل على الكلمات المفردات بأكملها ، والتي غالبًا ما تكون كبيرة (10 5-10 7 ).
دالة خسارة تقدير التباين للضوضاء (NCE) هي تقريب فعال للحصول على softmax كامل. بهدف تعلم تضمين الكلمة بدلاً من نمذجة توزيع الكلمات ، يمكن تبسيط خسارة NCE لاستخدام العينات السلبية.
يتمثل الهدف المبسط لأخذ العينات السلبية لكلمة مستهدفة في التمييز بين كلمة السياق والعينات السلبية المستمدة من num_ns
من توزيع الضوضاء P n (w) للكلمات. بتعبير أدق ، فإن التقريب الفعال لـ softmax الكامل على المفردات هو ، لزوج تخطي الجرام ، لفرض الخسارة لكلمة مستهدفة كمشكلة تصنيف بين كلمة السياق والعينات السلبية num_ns
.
يتم تعريف العينة السالبة على أنها زوج (target_word, context_word)
بحيث لا تظهر context_word
في window_size
المجاورة لـ target_word
. بالنسبة لجملة المثال ، هذه بعض العينات السلبية المحتملة (عندما يكون window_size
2
).
(hot, shimmered)
(wide, hot)
(wide, sun)
في القسم التالي ، ستنشئ عينات متخطية وعينات سالبة لجملة واحدة. ستتعرف أيضًا على تقنيات أخذ العينات الفرعية وتدريب نموذج تصنيف لأمثلة تدريب إيجابية وسلبية لاحقًا في البرنامج التعليمي.
يثبت
import io
import re
import string
import tqdm
import numpy as np
import tensorflow as tf
from tensorflow.keras import layers
# Load the TensorBoard notebook extension
%load_ext tensorboard
SEED = 42
AUTOTUNE = tf.data.AUTOTUNE
تحويل جملة كمثال
تأمل الجملة التالية:
كان الطريق الواسع يتلألأ في الشمس الحارقة.
رمزية الجملة:
sentence = "The wide road shimmered in the hot sun"
tokens = list(sentence.lower().split())
print(len(tokens))
8
قم بإنشاء مفردات لحفظ التعيينات من الرموز المميزة إلى فهارس الأعداد الصحيحة:
vocab, index = {}, 1 # start indexing from 1
vocab['<pad>'] = 0 # add a padding token
for token in tokens:
if token not in vocab:
vocab[token] = index
index += 1
vocab_size = len(vocab)
print(vocab)
{'<pad>': 0, 'the': 1, 'wide': 2, 'road': 3, 'shimmered': 4, 'in': 5, 'hot': 6, 'sun': 7}
قم بإنشاء مفردات معكوسة لحفظ التعيينات من فهارس الأعداد الصحيحة إلى الرموز المميزة:
inverse_vocab = {index: token for token, index in vocab.items()}
print(inverse_vocab)
{0: '<pad>', 1: 'the', 2: 'wide', 3: 'road', 4: 'shimmered', 5: 'in', 6: 'hot', 7: 'sun'}
تحويل الجملة الخاصة بك:
example_sequence = [vocab[word] for word in tokens]
print(example_sequence)
[1, 2, 3, 4, 5, 1, 6, 7]
توليد تخطي غرام من جملة واحدة
توفر الوحدة النمطية tf.keras.preprocessing.sequence
وظائف مفيدة تسهل إعداد البيانات لـ word2vec. يمكنك استخدام tf.keras.preprocessing.sequence.skipgrams
لتوليد أزواج تخطي الجرام من example_sequence
مع window_size
معطى من الرموز المميزة في النطاق [0, vocab_size)
.
window_size = 2
positive_skip_grams, _ = tf.keras.preprocessing.sequence.skipgrams(
example_sequence,
vocabulary_size=vocab_size,
window_size=window_size,
negative_samples=0)
print(len(positive_skip_grams))
26
اطبع عددًا قليلاً من التخطي الإيجابي:
for target, context in positive_skip_grams[:5]:
print(f"({target}, {context}): ({inverse_vocab[target]}, {inverse_vocab[context]})")
(2, 3): (wide, road) (5, 3): (in, road) (4, 2): (shimmered, wide) (1, 7): (the, sun) (4, 1): (shimmered, the)
أخذ العينات السلبية لتخطي غرام واحد
ترجع الدالة skipgrams
جميع أزواج التخطي الموجبة عن طريق التمرير فوق فترة نافذة معينة. لإنتاج أزواج إضافية من التخطي تكون بمثابة عينات سلبية للتدريب ، تحتاج إلى أخذ عينات من الكلمات العشوائية من المفردات. استخدم الدالة tf.random.log_uniform_candidate_sampler
لتجربة عدد num_ns
من العينات السالبة لكلمة هدف معينة في النافذة. يمكنك استدعاء الوظيفة على كلمة واحدة هدف تخطي جرام وتمرير كلمة السياق كفئة حقيقية لاستبعادها من أخذ العينات.
# Get target and context words for one positive skip-gram.
target_word, context_word = positive_skip_grams[0]
# Set the number of negative samples per positive context.
num_ns = 4
context_class = tf.reshape(tf.constant(context_word, dtype="int64"), (1, 1))
negative_sampling_candidates, _, _ = tf.random.log_uniform_candidate_sampler(
true_classes=context_class, # class that should be sampled as 'positive'
num_true=1, # each positive skip-gram has 1 positive context class
num_sampled=num_ns, # number of negative context words to sample
unique=True, # all the negative samples should be unique
range_max=vocab_size, # pick index of the samples from [0, vocab_size]
seed=SEED, # seed for reproducibility
name="negative_sampling" # name of this operation
)
print(negative_sampling_candidates)
print([inverse_vocab[index.numpy()] for index in negative_sampling_candidates])
tf.Tensor([2 1 4 3], shape=(4,), dtype=int64) ['wide', 'the', 'shimmered', 'road']
بناء مثال تدريب واحد
بالنسبة إلى skip-gram إيجابية معينة (target_word, context_word)
، لديك الآن أيضًا عدد num_ns
من كلمات سياق عينة سلبية لا تظهر في حي حجم النافذة من target_word
. num_ns
كلمات السياق الإيجابية 1
و context_word
من كلمات السياق السلبية في موتر واحد. ينتج عن هذا مجموعة من الجرامات التخطي الإيجابية (المسمى 1
) والعينات السلبية (المسمى 0
) لكل كلمة مستهدفة.
# Add a dimension so you can use concatenation (in the next step).
negative_sampling_candidates = tf.expand_dims(negative_sampling_candidates, 1)
# Concatenate a positive context word with negative sampled words.
context = tf.concat([context_class, negative_sampling_candidates], 0)
# Label the first context word as `1` (positive) followed by `num_ns` `0`s (negative).
label = tf.constant([1] + [0]*num_ns, dtype="int64")
# Reshape the target to shape `(1,)` and context and label to `(num_ns+1,)`.
target = tf.squeeze(target_word)
context = tf.squeeze(context)
label = tf.squeeze(label)
تحقق من السياق والتسميات المقابلة للكلمة الهدف من مثال تخطي الجرام أعلاه:
print(f"target_index : {target}")
print(f"target_word : {inverse_vocab[target_word]}")
print(f"context_indices : {context}")
print(f"context_words : {[inverse_vocab[c.numpy()] for c in context]}")
print(f"label : {label}")
target_index : 2 target_word : wide context_indices : [3 2 1 4 3] context_words : ['road', 'wide', 'the', 'shimmered', 'road'] label : [1 0 0 0 0]
تشكل مجموعة الموترات (target, context, label)
مثالاً تدريبياً واحدًا لتدريب نموذج word2vec الخاص بأخذ العينات السلبية في الجرام. لاحظ أن الهدف من الشكل (1,)
بينما السياق والتسمية من الشكل (1+num_ns,)
print("target :", target)
print("context :", context)
print("label :", label)
target : tf.Tensor(2, shape=(), dtype=int32) context : tf.Tensor([3 2 1 4 3], shape=(5,), dtype=int64) label : tf.Tensor([1 0 0 0 0], shape=(5,), dtype=int64)
ملخص
يلخص هذا الرسم البياني الإجراء الخاص بتوليد مثال تدريبي من جملة:
لاحظ أن temperature
الكلمات code
ليسا جزءًا من جملة الإدخال. إنها تنتمي إلى المفردات مثل بعض المؤشرات الأخرى المستخدمة في الرسم البياني أعلاه.
تجميع كل الخطوات في وظيفة واحدة
جدول أخذ العينات تخطي الجرام
مجموعة البيانات الكبيرة تعني مفردات أكبر مع عدد أكبر من الكلمات الأكثر تكرارًا مثل كلمات التوقف. لا تضيف الأمثلة التدريبية التي تم الحصول on
the
is
مفيدة كثيرة للنموذج للتعلم منها. ميكولوف وآخرون. يقترح أخذ عينات فرعية من الكلمات المتكررة كممارسة مفيدة لتحسين جودة التضمين.
تقبل الدالة tf.keras.preprocessing.sequence.skipgrams
وسيطة جدول أخذ العينات لتشفير احتمالات أخذ عينات من أي رمز مميز. يمكنك استخدام tf.keras.preprocessing.sequence.make_sampling_table
لتوليد جدول أخذ العينات الاحتمالية المستند إلى ترتيب الكلمات وتمريره إلى وظيفة skipgrams
. افحص احتمالات أخذ العينات vocab_size
10.
sampling_table = tf.keras.preprocessing.sequence.make_sampling_table(size=10)
print(sampling_table)
[0.00315225 0.00315225 0.00547597 0.00741556 0.00912817 0.01068435 0.01212381 0.01347162 0.01474487 0.0159558 ]
يشير sampling_table[i]
إلى احتمال أخذ عينات من الكلمة i الأكثر شيوعًا في مجموعة البيانات. تفترض الوظيفة توزيع Zipf لكلمات ترددات أخذ العينات.
توليد بيانات التدريب
قم بتجميع جميع الخطوات الموضحة أعلاه في وظيفة يمكن استدعاؤها في قائمة الجمل الموجهة التي تم الحصول عليها من أي مجموعة بيانات نصية. لاحظ أن جدول أخذ العينات قد تم إنشاؤه قبل أخذ العينات من أزواج الكلمات المتخطية الجرام. سوف تستخدم هذه الوظيفة في الأقسام اللاحقة.
# Generates skip-gram pairs with negative sampling for a list of sequences
# (int-encoded sentences) based on window size, number of negative samples
# and vocabulary size.
def generate_training_data(sequences, window_size, num_ns, vocab_size, seed):
# Elements of each training example are appended to these lists.
targets, contexts, labels = [], [], []
# Build the sampling table for `vocab_size` tokens.
sampling_table = tf.keras.preprocessing.sequence.make_sampling_table(vocab_size)
# Iterate over all sequences (sentences) in the dataset.
for sequence in tqdm.tqdm(sequences):
# Generate positive skip-gram pairs for a sequence (sentence).
positive_skip_grams, _ = tf.keras.preprocessing.sequence.skipgrams(
sequence,
vocabulary_size=vocab_size,
sampling_table=sampling_table,
window_size=window_size,
negative_samples=0)
# Iterate over each positive skip-gram pair to produce training examples
# with a positive context word and negative samples.
for target_word, context_word in positive_skip_grams:
context_class = tf.expand_dims(
tf.constant([context_word], dtype="int64"), 1)
negative_sampling_candidates, _, _ = tf.random.log_uniform_candidate_sampler(
true_classes=context_class,
num_true=1,
num_sampled=num_ns,
unique=True,
range_max=vocab_size,
seed=SEED,
name="negative_sampling")
# Build context and label vectors (for one target word)
negative_sampling_candidates = tf.expand_dims(
negative_sampling_candidates, 1)
context = tf.concat([context_class, negative_sampling_candidates], 0)
label = tf.constant([1] + [0]*num_ns, dtype="int64")
# Append each element from the training example to global lists.
targets.append(target_word)
contexts.append(context)
labels.append(label)
return targets, contexts, labels
قم بإعداد بيانات التدريب لـ word2vec
من خلال فهم كيفية العمل مع جملة واحدة لنموذج word2vec بأخذ عينات سلبية تخطي الجرام ، يمكنك المتابعة لإنشاء أمثلة تدريبية من قائمة أكبر من الجمل!
تنزيل مجموعة النصوص
ستستخدم ملفًا نصيًا لكتابات شكسبير في هذا البرنامج التعليمي. قم بتغيير السطر التالي لتشغيل هذا الرمز على بياناتك الخاصة.
path_to_file = tf.keras.utils.get_file('shakespeare.txt', 'https://storage.googleapis.com/download.tensorflow.org/data/shakespeare.txt')
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/shakespeare.txt 1122304/1115394 [==============================] - 0s 0us/step 1130496/1115394 [==============================] - 0s 0us/step
اقرأ النص من الملف واطبع الأسطر القليلة الأولى:
with open(path_to_file) as f:
lines = f.read().splitlines()
for line in lines[:20]:
print(line)
First Citizen: Before we proceed any further, hear me speak. All: Speak, speak. First Citizen: You are all resolved rather to die than to famish? All: Resolved. resolved. First Citizen: First, you know Caius Marcius is chief enemy to the people. All: We know't, we know't. First Citizen: Let us kill him, and we'll have corn at our own price.
استخدم الأسطر غير الفارغة لإنشاء كائن tf.data.TextLineDataset
للخطوات التالية:
text_ds = tf.data.TextLineDataset(path_to_file).filter(lambda x: tf.cast(tf.strings.length(x), bool))
تحويل الجمل من المجموعة
يمكنك استخدام طبقة TextVectorization
لتوجيه الجمل من الجسم. تعرف على المزيد حول استخدام هذه الطبقة في هذا البرنامج التعليمي لتصنيف النص . لاحظ من الجمل القليلة الأولى أعلاه أن النص يجب أن يكون في حالة واحدة ويجب إزالة علامات الترقيم. للقيام بذلك ، حدد custom_standardization function
يمكن استخدامها في طبقة TextVectorization.
# Now, create a custom standardization function to lowercase the text and
# remove punctuation.
def custom_standardization(input_data):
lowercase = tf.strings.lower(input_data)
return tf.strings.regex_replace(lowercase,
'[%s]' % re.escape(string.punctuation), '')
# Define the vocabulary size and the number of words in a sequence.
vocab_size = 4096
sequence_length = 10
# Use the `TextVectorization` layer to normalize, split, and map strings to
# integers. Set the `output_sequence_length` length to pad all samples to the
# same length.
vectorize_layer = layers.TextVectorization(
standardize=custom_standardization,
max_tokens=vocab_size,
output_mode='int',
output_sequence_length=sequence_length)
استدعاء TextVectorization.adapt
على مجموعة البيانات النصية لإنشاء المفردات.
vectorize_layer.adapt(text_ds.batch(1024))
بمجرد تكييف حالة الطبقة لتمثيل مجموعة النص ، يمكن الوصول إلى المفردات باستخدام TextVectorization.get_vocabulary
. تعرض هذه الوظيفة قائمة بجميع الرموز المميزة للمفردات المصنفة (تنازليًا) حسب ترددها.
# Save the created vocabulary for reference.
inverse_vocab = vectorize_layer.get_vocabulary()
print(inverse_vocab[:20])
['', '[UNK]', 'the', 'and', 'to', 'i', 'of', 'you', 'my', 'a', 'that', 'in', 'is', 'not', 'for', 'with', 'me', 'it', 'be', 'your']
يمكن الآن استخدام vectorize_layer
لإنشاء ناقلات لكل عنصر في text_ds
(a tf.data.Dataset
). قم Dataset.batch
و Dataset.prefetch
و Dataset.map
و Dataset.unbatch
.
# Vectorize the data in text_ds.
text_vector_ds = text_ds.batch(1024).prefetch(AUTOTUNE).map(vectorize_layer).unbatch()
الحصول على تسلسلات من مجموعة البيانات
لديك الآن tf.data.Dataset
من الجمل المشفرة بشكل صحيح. لإعداد مجموعة البيانات لتدريب نموذج word2vec ، اعمل على تسوية مجموعة البيانات في قائمة متواليات متجهات الجملة. هذه الخطوة مطلوبة كما تفعل مع تكرار كل جملة في مجموعة البيانات لإنتاج أمثلة إيجابية وسلبية.
sequences = list(text_vector_ds.as_numpy_iterator())
print(len(sequences))
32777
افحص بعض الأمثلة من sequences
:
for seq in sequences[:5]:
print(f"{seq} => {[inverse_vocab[i] for i in seq]}")
[ 89 270 0 0 0 0 0 0 0 0] => ['first', 'citizen', '', '', '', '', '', '', '', ''] [138 36 982 144 673 125 16 106 0 0] => ['before', 'we', 'proceed', 'any', 'further', 'hear', 'me', 'speak', '', ''] [34 0 0 0 0 0 0 0 0 0] => ['all', '', '', '', '', '', '', '', '', ''] [106 106 0 0 0 0 0 0 0 0] => ['speak', 'speak', '', '', '', '', '', '', '', ''] [ 89 270 0 0 0 0 0 0 0 0] => ['first', 'citizen', '', '', '', '', '', '', '', '']
توليد أمثلة تدريبية من التسلسلات
sequences
هي الآن قائمة الجمل المشفرة. ما عليك سوى استدعاء دالة generate_training_data
تم تحديدها مسبقًا لإنشاء أمثلة تدريبية لنموذج word2vec. للتلخيص ، تتكرر الوظيفة فوق كل كلمة من كل سلسلة لتجميع كلمات السياق الإيجابية والسلبية. يجب أن يكون طول الهدف والسياقات والتسميات هو نفسه ، بحيث يمثل العدد الإجمالي لأمثلة التدريب.
targets, contexts, labels = generate_training_data(
sequences=sequences,
window_size=2,
num_ns=4,
vocab_size=vocab_size,
seed=SEED)
targets = np.array(targets)
contexts = np.array(contexts)[:,:,0]
labels = np.array(labels)
print('\n')
print(f"targets.shape: {targets.shape}")
print(f"contexts.shape: {contexts.shape}")
print(f"labels.shape: {labels.shape}")
100%|██████████| 32777/32777 [00:40<00:00, 811.35it/s] targets.shape: (66005,) contexts.shape: (66005, 5) labels.shape: (66005, 5)
تكوين مجموعة البيانات للأداء
لإجراء تجميع فعال لعدد كبير من أمثلة التدريب ، استخدم tf.data.Dataset
API. بعد هذه الخطوة ، سيكون لديك كائن tf.data.Dataset
من (target_word, context_word), (label)
عناصر لتدريب نموذج word2vec الخاص بك!
BATCH_SIZE = 1024
BUFFER_SIZE = 10000
dataset = tf.data.Dataset.from_tensor_slices(((targets, contexts), labels))
dataset = dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE, drop_remainder=True)
print(dataset)
<BatchDataset element_spec=((TensorSpec(shape=(1024,), dtype=tf.int64, name=None), TensorSpec(shape=(1024, 5), dtype=tf.int64, name=None)), TensorSpec(shape=(1024, 5), dtype=tf.int64, name=None))>
تطبيق Dataset.cache
و Dataset.prefetch
لتحسين الأداء:
dataset = dataset.cache().prefetch(buffer_size=AUTOTUNE)
print(dataset)
<PrefetchDataset element_spec=((TensorSpec(shape=(1024,), dtype=tf.int64, name=None), TensorSpec(shape=(1024, 5), dtype=tf.int64, name=None)), TensorSpec(shape=(1024, 5), dtype=tf.int64, name=None))>
النموذج والتدريب
يمكن تنفيذ نموذج word2vec كمصنف للتمييز بين كلمات السياق الحقيقية من التخطي بالجرام وكلمات السياق الخاطئة التي تم الحصول عليها من خلال أخذ العينات السلبية. يمكنك إجراء عملية ضرب المنتج النقطي بين عمليات تضمين كلمات الهدف والسياق للحصول على تنبؤات للتسميات وحساب وظيفة الخسارة مقابل التسميات الحقيقية في مجموعة البيانات.
نموذج word2vec مصنف فرعي
استخدم واجهة برمجة تطبيقات Keras Subclassing لتعريف نموذج word2vec الخاص بك بالطبقات التالية:
-
target_embedding
: طبقةtf.keras.layers.Embedding
، والتي تبحث عن تضمين كلمة عندما تظهر ككلمة مستهدفة. عدد المعلمات في هذه الطبقة(vocab_size * embedding_dim)
. -
context_embedding
: طبقةtf.keras.layers.Embedding
أخرى ، والتي تبحث عن تضمين كلمة عندما تظهر ككلمة سياق. عدد المعلمات في هذه الطبقة هو نفسه الموجود فيtarget_embedding
، أي(vocab_size * embedding_dim)
. -
dots
: طبقةtf.keras.layers.Dot
التي تحسب المنتج النقطي لعمليات دمج الهدف والسياق من زوج تدريب. -
flatten
: طبقة تسطيحtf.keras.layers.Flatten
. لتسطيح نتائج طبقةdots
في لوغيتس.
باستخدام نموذج الفئة الفرعية ، يمكنك تحديد دالة call()
التي تقبل أزواج (target, context)
والتي يمكن بعد ذلك تمريرها إلى طبقة التضمين المقابلة لها. أعد تشكيل context_embedding
لأداء منتج نقطي باستخدام target_embedding
وإرجاع النتيجة المسطحة.
class Word2Vec(tf.keras.Model):
def __init__(self, vocab_size, embedding_dim):
super(Word2Vec, self).__init__()
self.target_embedding = layers.Embedding(vocab_size,
embedding_dim,
input_length=1,
name="w2v_embedding")
self.context_embedding = layers.Embedding(vocab_size,
embedding_dim,
input_length=num_ns+1)
def call(self, pair):
target, context = pair
# target: (batch, dummy?) # The dummy axis doesn't exist in TF2.7+
# context: (batch, context)
if len(target.shape) == 2:
target = tf.squeeze(target, axis=1)
# target: (batch,)
word_emb = self.target_embedding(target)
# word_emb: (batch, embed)
context_emb = self.context_embedding(context)
# context_emb: (batch, context, embed)
dots = tf.einsum('be,bce->bc', word_emb, context_emb)
# dots: (batch, context)
return dots
تحديد دالة الخسارة وتجميع النموذج
للتبسيط ، يمكنك استخدام tf.keras.losses.CategoricalCrossEntropy
كبديل لفقدان العينة السلبي. إذا كنت ترغب في كتابة وظيفة الخسارة المخصصة الخاصة بك ، فيمكنك أيضًا القيام بذلك على النحو التالي:
def custom_loss(x_logit, y_true):
return tf.nn.sigmoid_cross_entropy_with_logits(logits=x_logit, labels=y_true)
حان الوقت لبناء النموذج الخاص بك! قم بإنشاء فئة word2vec الخاصة بك مع بُعد تضمين 128 (يمكنك تجربة قيم مختلفة). قم بتجميع النموذج باستخدام محسن tf.keras.optimizers.Adam
.
embedding_dim = 128
word2vec = Word2Vec(vocab_size, embedding_dim)
word2vec.compile(optimizer='adam',
loss=tf.keras.losses.CategoricalCrossentropy(from_logits=True),
metrics=['accuracy'])
حدد أيضًا رد اتصال لتسجيل إحصائيات التدريب لـ Tensorboard:
tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir="logs")
تدريب النموذج على dataset
لعدد من الحقب:
word2vec.fit(dataset, epochs=20, callbacks=[tensorboard_callback])
Epoch 1/20 64/64 [==============================] - 1s 4ms/step - loss: 1.6081 - accuracy: 0.2343 Epoch 2/20 64/64 [==============================] - 0s 3ms/step - loss: 1.5878 - accuracy: 0.5475 Epoch 3/20 64/64 [==============================] - 0s 3ms/step - loss: 1.5381 - accuracy: 0.5813 Epoch 4/20 64/64 [==============================] - 0s 3ms/step - loss: 1.4545 - accuracy: 0.5620 Epoch 5/20 64/64 [==============================] - 0s 3ms/step - loss: 1.3567 - accuracy: 0.5764 Epoch 6/20 64/64 [==============================] - 0s 3ms/step - loss: 1.2603 - accuracy: 0.6070 Epoch 7/20 64/64 [==============================] - 0s 3ms/step - loss: 1.1703 - accuracy: 0.6403 Epoch 8/20 64/64 [==============================] - 0s 3ms/step - loss: 1.0866 - accuracy: 0.6756 Epoch 9/20 64/64 [==============================] - 0s 3ms/step - loss: 1.0090 - accuracy: 0.7087 Epoch 10/20 64/64 [==============================] - 0s 3ms/step - loss: 0.9368 - accuracy: 0.7371 Epoch 11/20 64/64 [==============================] - 0s 3ms/step - loss: 0.8699 - accuracy: 0.7631 Epoch 12/20 64/64 [==============================] - 0s 3ms/step - loss: 0.8081 - accuracy: 0.7849 Epoch 13/20 64/64 [==============================] - 0s 3ms/step - loss: 0.7512 - accuracy: 0.8052 Epoch 14/20 64/64 [==============================] - 0s 3ms/step - loss: 0.6991 - accuracy: 0.8230 Epoch 15/20 64/64 [==============================] - 0s 3ms/step - loss: 0.6514 - accuracy: 0.8378 Epoch 16/20 64/64 [==============================] - 0s 3ms/step - loss: 0.6079 - accuracy: 0.8517 Epoch 17/20 64/64 [==============================] - 0s 3ms/step - loss: 0.5683 - accuracy: 0.8641 Epoch 18/20 64/64 [==============================] - 0s 3ms/step - loss: 0.5322 - accuracy: 0.8747 Epoch 19/20 64/64 [==============================] - 0s 3ms/step - loss: 0.4994 - accuracy: 0.8844 Epoch 20/20 64/64 [==============================] - 0s 3ms/step - loss: 0.4695 - accuracy: 0.8935 <keras.callbacks.History at 0x7fc21c237050>
يعرض Tensorboard الآن دقة نموذج word2vec وخسارة:
#docs_infra: no_execute
%tensorboard --logdir logs
تضمين البحث والتحليل
احصل على الأوزان من النموذج باستخدام Model.get_layer
و Layer.get_weights
. توفر وظيفة TextVectorization.get_vocabulary
المفردات لبناء ملف بيانات وصفية برمز واحد لكل سطر.
weights = word2vec.get_layer('w2v_embedding').get_weights()[0]
vocab = vectorize_layer.get_vocabulary()
إنشاء وحفظ المتجهات وملفات البيانات الوصفية:
out_v = io.open('vectors.tsv', 'w', encoding='utf-8')
out_m = io.open('metadata.tsv', 'w', encoding='utf-8')
for index, word in enumerate(vocab):
if index == 0:
continue # skip 0, it's padding.
vec = weights[index]
out_v.write('\t'.join([str(x) for x in vec]) + "\n")
out_m.write(word + "\n")
out_v.close()
out_m.close()
قم بتنزيل vectors.tsv
و metadata.tsv
لتحليل حفلات الزفاف التي تم الحصول عليها في جهاز عرض التضمين :
try:
from google.colab import files
files.download('vectors.tsv')
files.download('metadata.tsv')
except Exception:
pass
الخطوات التالية
لقد أوضح لك هذا البرنامج التعليمي كيفية تنفيذ نموذج word2vec بتخطي الجرام مع أخذ عينات سلبية من نقطة الصفر وتصور كلمة التضمينات التي تم الحصول عليها.
لمعرفة المزيد حول متجهات الكلمات وتمثيلاتها الرياضية ، ارجع إلى هذه الملاحظات .
لمعرفة المزيد حول المعالجة المتقدمة للنصوص ، اقرأ نموذج Transformer لفهم اللغة التعليمية.
إذا كنت مهتمًا بنماذج التضمين المُدرَّبة مسبقًا ، فقد تكون مهتمًا أيضًا باستكشاف TF-Hub CORD-19 Swivel Embeddings أو التشفير الشامل متعدد اللغات .
قد ترغب أيضًا في تدريب النموذج على مجموعة بيانات جديدة (يتوفر العديد منها في مجموعات بيانات TensorFlow ).