الاستماع إلى المرأة الأولى في الندوة ML هذا الثلاثاء 19 أكتوبر في 09:00 PST سجل الآن

Word2Vec

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

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

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

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

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

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

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

تأمل الجملة التالية المكونة من 8 كلمات.

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

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

word2vec_skipgrams

الهدف التدريبي لنموذج تخطي الجرام هو تعظيم احتمالية التنبؤ بكلمات السياق بالنظر إلى الكلمة المستهدفة. لسلسلة من الكلمات ث ث ... ث والهدف كما يمكن كتابة متوسط احتمال سجل

word2vec_skipgram_objective

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

word2vec_full_softmax

حيث الخامس والخامس 'والهدف وناقلات السياق تمثيل الكلمات وW هو حجم المفردات.

حساب القاسم من هذه الصيغة تنطوي على أداء softmax كامل على المفردات كلها التي غالبا ما تكون كبيرة (10 5 -10 7) شروط.

و الضوضاء التقابلي تقدير فقدان الوظيفة تقريبي فعالة لsoftmax بالكامل. مع هدف لمعرفة كلمة التضمينات بدلا من نمذجة توزيع كلمة، وفقدان NCE يمكن مبسطة لاستخدام العينات سلبية.

السلبية مبسطة أخذ العينات الهدف عن كلمة الهدف هو التمييز بين كلمة السياق من عينات num_ns سلبية مستمدة من توزيع الضوضاء P ن (ث) من الكلمات. بتعبير أدق، تقريبي كفاءة 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

تحويل جملة كمثال

تأمل الجملة التالية:
The wide road shimmered in the hot sun.

رمزية الجملة:

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]})")
(3, 1): (road, the)
(6, 1): (hot, the)
(2, 1): (wide, the)
(6, 7): (hot, sun)
(6, 5): (hot, in)

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

و 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']

قم ببناء مثال تدريبي واحد

لإيجابية نظرا (target_word, context_word) تخطي غرام، لديك الآن أيضا num_ns سلبية عينات الكلمات السياق التي لا تظهر في حي حجم نافذة target_word . الدفعي 1 الإيجابية context_word و num_ns سلبية الكلمات السياق إلى موتر واحد. هذا وتنتج مجموعة من تخطي غراما إيجابية (وصفت بانها 1 ) والعينات السلبية (وصفت بأنها 0 ) لكل كلمة المستهدفة.

# Add a dimension so you can use concatenation (on the next step).
negative_sampling_candidates = tf.expand_dims(negative_sampling_candidates, 1)

# Concat positive context word with negative sampled words.
context = tf.concat([context_class, negative_sampling_candidates], 0)

# Label first context word as 1 (positive) followed by num_ns 0s (negative).
label = tf.constant([1] + [0]*num_ns, dtype="int64")

# Reshape 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    : 3
target_word     : road
context_indices : [1 2 1 4 3]
context_words   : ['the', 'wide', 'the', 'shimmered', 'road']
label           : [1 0 0 0 0]

A الصفوف (tuple) من (target, context, label) التنسورات تشكل المثال تدريبية واحدة لتدريب الخاص بك سلبية تخطي غرام أخذ العينات نموذج Word2Vec. لاحظ أن الهدف من الشكل (1,) في حين أن السياق والتسمية والشكل (1+num_ns,)

print("target  :", target)
print("context :", context)
print("label   :", label)
target  : tf.Tensor(3, shape=(), dtype=int32)
context : tf.Tensor([1 2 1 4 3], shape=(5,), dtype=int64)
label   : tf.Tensor([1 0 0 0 0], shape=(5,), dtype=int64)

ملخص

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

word2vec_negative_sampling

تجميع كل الخطوات في وظيفة واحدة

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

مجموعة البيانات الكبيرة تعني مفردات أكبر مع عدد أكبر من الكلمات الأكثر تكرارًا مثل كلمات التوقف. الأمثلة التدريبية التي تم الحصول عليها من العينات الكلمات التي تحدث عادة (مثل the ، is ، on ) لا تضيف الكثير من المعلومات المفيدة للنموذج للتعلم من. ميكولوف وآخرون. يقترح أخذ عينات فرعية من الكلمات المتكررة كممارسة مفيدة لتحسين جودة التضمين.

و 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] يدل على احتمال أخذ عينات من ط ال معظم كلمة شائعة في مجموعة بيانات. وتفترض دالة التوزيع زيبف و الترددات كلمة لأخذ العينات.

توليد بيانات التدريب

قم بتجميع جميع الخطوات الموضحة أعلاه في وظيفة يمكن استدعاؤها في قائمة الجمل الموجهة التي تم الحصول عليها من أي مجموعة بيانات نصية. لاحظ أن جدول أخذ العينات قد تم إنشاؤه قبل أخذ العينات من أزواج الكلمات ذات الجرام التخطي. سوف تستخدم هذه الوظيفة في الأقسام اللاحقة.

# 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 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 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 طبقة إلى vectorize الجمل من المحكمة. معرفة المزيد حول استخدام هذه الطبقة في هذا تصنيف النص التعليمي. لاحظ من الجمل القليلة الأولى أعلاه أن النص يجب أن يكون في حالة واحدة ويجب إزالة علامات الترقيم. للقيام بذلك، تحديد 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 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 output_sequence_length length to pad all samples to same length.
vectorize_layer = layers.TextVectorization(
    standardize=custom_standardization,
    max_tokens=vocab_size,
    output_mode='int',
    output_sequence_length=sequence_length)

اتصل adapt على مجموعة البيانات النص على خلق المفردات.

vectorize_layer.adapt(text_ds.batch(1024))

مرة واحدة وقد تم تكييف حالة طبقة لتمثيل جسم النص، ويمكن الوصول إليها من المفردات مع 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 .

# 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:33<00:00, 976.91it/s]
targets.shape: (64889,)
contexts.shape: (64889, 5)
labels.shape: (64889, 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 shapes: (((1024,), (1024, 5)), (1024, 5)), types: ((tf.int64, tf.int64), tf.int64)>

إضافة cache() و prefetch() لتحسين الأداء.

dataset = dataset.cache().prefetch(buffer_size=AUTOTUNE)
print(dataset)
<PrefetchDataset shapes: (((1024,), (1024, 5)), (1024, 5)), types: ((tf.int64, tf.int64), tf.int64)>

النموذج والتدريب

يمكن تنفيذ نموذج Word2Vec كمصنف للتمييز بين كلمات السياق الحقيقية من التخطي والكلمات السياقية الخاطئة التي تم الحصول عليها من خلال أخذ العينات السلبية. يمكنك تنفيذ منتج نقطي بين عمليات تضمين كلمات الهدف والسياق للحصول على تنبؤات للتسميات وحساب الخسارة مقابل التسميات الحقيقية في مجموعة البيانات.

نموذج Word2Vec مصنف فرعيًا

استخدام API Keras إن شاء subclasses ترث لتحديد نموذج Word2Vec مع الطبقات التالية:

  • target_embedding : A tf.keras.layers.Embedding طبقة التي بالبحث عن تضمين الكلمة عند ظهوره ككلمة الهدف. عدد المعلمات في هذه الطبقة هم (vocab_size * embedding_dim) .
  • context_embedding : آخر tf.keras.layers.Embedding طبقة التي بالبحث عن تضمين الكلمة عند ظهوره ككلمة السياق. عدد المعلمات في هذه الطبقة هي نفسها كما هو الحال في target_embedding ، أي (vocab_size * embedding_dim) .
  • dots : A tf.keras.layers.Dot الطبقة التي يحسب المنتج نقطة من التضمينات الهدف والسياق من زوج التدريب.
  • flatten : A tf.keras.layers.Flatten طبقة لشد نتائج dots طبقة إلى logits.

مع النموذج subclassed، يمكنك تحديد 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
63/63 [==============================] - 1s 9ms/step - loss: 1.6083 - accuracy: 0.2309
Epoch 2/20
63/63 [==============================] - 0s 2ms/step - loss: 1.5890 - accuracy: 0.5520
Epoch 3/20
63/63 [==============================] - 0s 3ms/step - loss: 1.5413 - accuracy: 0.6021
Epoch 4/20
63/63 [==============================] - 0s 2ms/step - loss: 1.4584 - accuracy: 0.5789
Epoch 5/20
63/63 [==============================] - 0s 2ms/step - loss: 1.3598 - accuracy: 0.5857
Epoch 6/20
63/63 [==============================] - 0s 2ms/step - loss: 1.2622 - accuracy: 0.6127
Epoch 7/20
63/63 [==============================] - 0s 2ms/step - loss: 1.1710 - accuracy: 0.6456
Epoch 8/20
63/63 [==============================] - 0s 2ms/step - loss: 1.0867 - accuracy: 0.6804
Epoch 9/20
63/63 [==============================] - 0s 3ms/step - loss: 1.0085 - accuracy: 0.7123
Epoch 10/20
63/63 [==============================] - 0s 2ms/step - loss: 0.9361 - accuracy: 0.7418
Epoch 11/20
63/63 [==============================] - 0s 2ms/step - loss: 0.8690 - accuracy: 0.7669
Epoch 12/20
63/63 [==============================] - 0s 2ms/step - loss: 0.8070 - accuracy: 0.7891
Epoch 13/20
63/63 [==============================] - 0s 2ms/step - loss: 0.7500 - accuracy: 0.8082
Epoch 14/20
63/63 [==============================] - 0s 3ms/step - loss: 0.6977 - accuracy: 0.8249
Epoch 15/20
63/63 [==============================] - 0s 3ms/step - loss: 0.6498 - accuracy: 0.8393
Epoch 16/20
63/63 [==============================] - 0s 3ms/step - loss: 0.6061 - accuracy: 0.8528
Epoch 17/20
63/63 [==============================] - 0s 3ms/step - loss: 0.5663 - accuracy: 0.8651
Epoch 18/20
63/63 [==============================] - 0s 3ms/step - loss: 0.5300 - accuracy: 0.8762
Epoch 19/20
63/63 [==============================] - 0s 2ms/step - loss: 0.4970 - accuracy: 0.8858
Epoch 20/20
63/63 [==============================] - 0s 2ms/step - loss: 0.4670 - accuracy: 0.8947
<keras.callbacks.History at 0x7fbea4362490>

تعرض Tensorboard الآن دقة نموذج Word2Vec وفقده.

%tensorboard --logdir logs

تضمين البحث والتحليل

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