word2vec

عرض على TensorFlow.org تشغيل في Google Colab عرض المصدر على جيثب تحميل دفتر

لا تعد word2vec خوارزمية مفردة ، بل هي عبارة عن مجموعة من البنى والتحسينات النموذجية التي يمكن استخدامها لتعلم تضمين الكلمات من مجموعات البيانات الكبيرة. لقد أثبتت عمليات التضمين التي تم تعلمها من خلال word2vec نجاحها في مجموعة متنوعة من مهام معالجة اللغة الطبيعية.

اقترحت هذه الأوراق طريقتين لتعلم تمثيل الكلمات:

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

ستستخدم أسلوب تخطي الجرام في هذا البرنامج التعليمي. أولاً ، سوف تستكشف التخطي والمفاهيم الأخرى باستخدام جملة واحدة للتوضيح. بعد ذلك ، ستقوم بتدريب نموذج word2vec الخاص بك على مجموعة بيانات صغيرة. يحتوي هذا البرنامج التعليمي أيضًا على رمز لتصدير حفلات الزفاف المدربة وتصورها في TensorFlow Embedding Projector .

تخطي الجرام وأخذ العينات السلبية

بينما يتنبأ نموذج كيس الكلمات بكلمة في ضوء السياق المجاور ، يتنبأ نموذج تخطي الجرام بالسياق (أو الجيران) للكلمة ، بالنظر إلى الكلمة نفسها. تم تدريب النموذج على تخطي الجرامات ، وهي n-grams تسمح بتخطي الرموز (انظر الرسم البياني أدناه للحصول على مثال). يمكن تمثيل سياق الكلمة من خلال مجموعة من أزواج skip-gram من (target_word, context_word) حيث يظهر context_word في السياق المجاور لـ target_word .

تأمل الجملة التالية المكونة من ثماني كلمات:

كان الطريق الواسع يتلألأ في الشمس الحارقة.

يتم تحديد كلمات السياق لكل كلمة من الكلمات الثمانية في هذه الجملة بحجم النافذة. يحدد حجم النافذة مدى الكلمات على جانبي target_word والتي يمكن اعتبارها context word . يوجد أدناه جدول تخطي جرامات الكلمات المستهدفة بناءً على أحجام النوافذ المختلفة.

word2vec_skipgrams

الهدف التدريبي لنموذج تخطي الجرام هو تعظيم احتمالية توقع كلمات السياق بالنظر إلى الكلمة المستهدفة. بالنسبة لسلسلة من الكلمات w 1 ، w 2 ، ... w T ، يمكن كتابة الهدف على أنه متوسط ​​احتمالية السجل

word2vec_skipgram_objective

حيث c هو حجم سياق التدريب. تحدد الصيغة الأساسية للتخطي هذا الاحتمال باستخدام دالة softmax.

word2vec_full_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)

ملخص

يلخص هذا الرسم البياني الإجراء الخاص بتوليد مثال تدريبي من جملة:

word2vec_negative_sampling

لاحظ أن 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 بتخطي الجرام مع أخذ عينات سلبية من نقطة الصفر وتصور كلمة التضمينات التي تم الحصول عليها.