עזרה להגן על שונית המחסום הגדולה עם TensorFlow על Kaggle הצטרפו אתגר

Word2Vec

הצג באתר TensorFlow.org הפעל בגוגל קולאב צפה במקור ב-GitHub הורד מחברת

Word2Vec אינו אלגוריתם יחיד, אלא משפחה של ארכיטקטורות מודלים ואופטימיזציות שניתן להשתמש בהן כדי ללמוד הטבעת מילים ממערכי נתונים גדולים. הטבעות שנלמדו באמצעות Word2Vec הוכחו כמוצלחות במגוון משימות עיבוד שפה טבעית במורד הזרם.

מאמרים אלה הציעו שתי שיטות ללימוד ייצוגים של מילים:

  • מודל רציף שק-אוף-מילים המנבא את המילה האמצעית על סמך מילות ההקשר שמסביב. ההקשר מורכב מכמה מילים לפני ואחרי המילה הנוכחית (האמצעית). ארכיטקטורה זו נקראת מודל שק של מילים מכיוון שסדר המילים בהקשר אינו חשוב.
  • Continuous Skip-gram Model אשר מנבא מילים בטווח מסוים לפני ואחרי המילה הנוכחית באותו משפט. דוגמה עובדת לכך ניתנת להלן.

אתה תשתמש בגישת הדילוג על גרם במדריך זה. ראשית, תחקור דילוג-גרמים ומושגים אחרים באמצעות משפט בודד להמחשה. לאחר מכן, תאמן את מודל Word2Vec משלך על מערך נתונים קטן. מדריך זה מכיל גם קוד לייצוא ההטמעות המאומנות ולהמחיש אותן במקרן הטבעת TensorFlow .

דילוג-גרם ודגימה שלילית

בעוד שמודל שק של מילים מנבא מילה בהינתן ההקשר השכן, מודל דילוג-גרם מנבא את ההקשר (או השכנים) של מילה, בהינתן המילה עצמה. המודל מאומן על דילוג-גרם, שהם n-גרם המאפשרים דילוג על אסימונים (ראה תרשים למטה לדוגמא). ניתן לייצג את ההקשר של מילה באמצעות קבוצה של זוגות דילוג-גרם של (target_word, context_word) כאשר context_word מופיעה בהקשר השכן של target_word .

שקול את המשפט הבא בן 8 מילים.

הדרך הרחבה נצצה בשמש הלוהטת.

מילות ההקשר של כל אחת מ-8 המילים של משפט זה מוגדרות על ידי גודל חלון. גודל החלון קובע את טווח המילים משני הצדדים של 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 ).

פונקציית אובדן הערכת ניגודיות רעש היא קירוב יעיל ל-softmax מלא. מתוך מטרה ללמוד הטבעת מילים במקום לעצב את התפלגות המילים, ניתן לפשט את אובדן ה-NCE כדי להשתמש בדגימה שלילית.

מטרת הדגימה השלילית הפשוטה של ​​מילת יעד היא להבחין בין מילת ההקשר לבין num_ns דגימות שליליות שנלקחו מהתפלגות רעש P n (w) של מילים. ליתר דיוק, קירוב יעיל של softmax מלא על פני אוצר המילים הוא, עבור זוג דילוג-גרם, להציב את ההפסד של מילת מטרה כבעיית סיווג בין מילת ההקשר לבין num_ns דוגמאות שליליות.

מדגם שלילי מוגדר כזוג (מילה_יעד, מילת_הקשר) כך שהמילה_הקשר אינה מופיעה בשכונת window_size של מילת_מטרה. עבור המשפט לדוגמה, אלו הן כמה דוגמאות שליליות פוטנציאליות (כאשר 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]})")
(2, 4): (wide, shimmered)
(1, 3): (the, road)
(3, 5): (road, in)
(6, 7): (hot, sun)
(5, 1): (in, 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']

בנה דוגמה אחת לאימון

עבור דילוג-גרם חיובי (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    : 2
target_word     : wide
context_indices : [4 2 1 4 3]
context_words   : ['shimmered', '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([4 2 1 4 3], shape=(5,), dtype=int64)
label   : tf.Tensor([1 0 0 0 0], shape=(5,), dtype=int64)

סיכום

תמונה זו מסכמת את ההליך של יצירת דוגמה לאימון ממשפט.

word2vec_sampling_negative

קומפלו את כל השלבים לפונקציה אחת

טבלת דגימה של דילוג על גרם

מערך נתונים גדול פירושו אוצר מילים גדול יותר עם מספר גבוה יותר של מילים תכופות יותר כגון מילות עצור. דוגמאות הדרכה המתקבלות מדגימת מילים הנפוצות (כגון 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] מציין את ההסתברות לדגימה של המילה ה-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 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 כדי לעצב משפטים מהקורפוס. למד עוד על השימוש בשכבה זו במדריך זה של סיווג טקסט . שימו לב מהמשפטים הראשונים למעלה שהטקסט צריך להיות במקרה אחד ויש להסיר סימני פיסוק. לשם כך, הגדר 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 היא כעת רשימה של משפטים מקודדים ב-int. פשוט קרא לפונקציה gener_training_data 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:38<00:00, 841.16it/s]
targets.shape: (65623,)
contexts.shape: (65623, 5)
labels.shape: (65623, 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))>

הוסף cache() ו- 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 API כדי להגדיר את מודל Word2Vec שלך עם השכבות הבאות:

  • target_embedding : שכבת tf.keras.layers.Embedding אשר מחפשת את הטבעה של מילה כשהיא מופיעה כמילת יעד. מספר הפרמטרים בשכבה זו הם (vocab_size * embedding_dim) .
  • context_embedding : שכבת tf.keras.layers.Embedding . Embedding נוספת שמחפשת את הטבעה של מילה כשהיא מופיעה כמילת הקשר. מספר הפרמטרים בשכבה זו זהה לאלו ב- target_embedding , כלומר (vocab_size * embedding_dim) .
  • dots : שכבת tf.keras.layers.Dot את תוצר הנקודות של הטמעות יעד והקשר מזוג אימון.
  • tf.keras.layers.Flatten 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.6082 - accuracy: 0.2325
Epoch 2/20
64/64 [==============================] - 0s 3ms/step - loss: 1.5883 - accuracy: 0.5519
Epoch 3/20
64/64 [==============================] - 0s 3ms/step - loss: 1.5392 - accuracy: 0.5956
Epoch 4/20
64/64 [==============================] - 0s 3ms/step - loss: 1.4551 - accuracy: 0.5715
Epoch 5/20
64/64 [==============================] - 0s 3ms/step - loss: 1.3565 - accuracy: 0.5788
Epoch 6/20
64/64 [==============================] - 0s 3ms/step - loss: 1.2595 - accuracy: 0.6057
Epoch 7/20
64/64 [==============================] - 0s 3ms/step - loss: 1.1691 - accuracy: 0.6394
Epoch 8/20
64/64 [==============================] - 0s 3ms/step - loss: 1.0854 - accuracy: 0.6738
Epoch 9/20
64/64 [==============================] - 0s 3ms/step - loss: 1.0078 - accuracy: 0.7062
Epoch 10/20
64/64 [==============================] - 0s 3ms/step - loss: 0.9359 - accuracy: 0.7366
Epoch 11/20
64/64 [==============================] - 0s 3ms/step - loss: 0.8693 - accuracy: 0.7632
Epoch 12/20
64/64 [==============================] - 0s 3ms/step - loss: 0.8078 - accuracy: 0.7845
Epoch 13/20
64/64 [==============================] - 0s 3ms/step - loss: 0.7512 - accuracy: 0.8045
Epoch 14/20
64/64 [==============================] - 0s 3ms/step - loss: 0.6994 - accuracy: 0.8210
Epoch 15/20
64/64 [==============================] - 0s 3ms/step - loss: 0.6519 - accuracy: 0.8360
Epoch 16/20
64/64 [==============================] - 0s 3ms/step - loss: 0.6086 - accuracy: 0.8499
Epoch 17/20
64/64 [==============================] - 0s 3ms/step - loss: 0.5691 - accuracy: 0.8622
Epoch 18/20
64/64 [==============================] - 0s 3ms/step - loss: 0.5331 - accuracy: 0.8731
Epoch 19/20
64/64 [==============================] - 0s 3ms/step - loss: 0.5003 - accuracy: 0.8833
Epoch 20/20
64/64 [==============================] - 0s 3ms/step - loss: 0.4705 - accuracy: 0.8918
<keras.callbacks.History at 0x7f002acb2990>

Tensorboard מציג כעת את הדיוק והאובדן של מודל Word2Vec.

#docs_infra: no_execute
%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 לדלג על גרם עם דגימה שלילית מאפס ולדמיין את הטבעות המילים שהתקבלו.