![]() | ![]() | ![]() | ![]() |
word2vec אינו אלגוריתם יחיד, אלא משפחה של ארכיטקטורות מודלים ואופטימיזציות שניתן להשתמש בהן כדי ללמוד הטבעת מילים ממערכי נתונים גדולים. הטמעות שנלמדו באמצעות word2vec הוכחו כמוצלחות במגוון משימות עיבוד שפה טבעית במורד הזרם.
מאמרים אלה הציעו שתי שיטות ללימוד ייצוגים של מילים:
- מודל שקית-מלים מתמשך : מנבא את המילה האמצעית בהתבסס על מילות ההקשר הסובבות. ההקשר מורכב מכמה מילים לפני ואחרי המילה הנוכחית (האמצעית). ארכיטקטורה זו נקראת מודל שק של מילים מכיוון שסדר המילים בהקשר אינו חשוב.
- מודל דילוג-גרם מתמשך : מנבא מילים בטווח מסוים לפני ואחרי המילה הנוכחית באותו משפט. דוגמה עובדת לכך מובאת להלן.
אתה תשתמש בגישת הדילוג על גרם במדריך זה. ראשית, תחקור דילוג-גרמים ומושגים אחרים באמצעות משפט בודד להמחשה. לאחר מכן, תאמן את מודל word2vec משלך על מערך נתונים קטן. מדריך זה מכיל גם קוד לייצוא ההטמעות המאומנות ולהמחיש אותן במקרן הטבעת TensorFlow .
דילוג על גרם ודגימה שלילית
בעוד שמודל שק של מילים מנבא מילה בהינתן ההקשר השכן, מודל דילוג-גרם מנבא את ההקשר (או השכנים) של מילה, בהינתן המילה עצמה. המודל מאומן על דילוג-גרם, שהם n-גרם המאפשרים דילוג על אסימונים (ראה תרשים למטה לדוגמא). ניתן לייצג את ההקשר של מילה באמצעות קבוצה של זוגות דילוג-גרם של (target_word, context_word)
כאשר context_word
מופיעה בהקשר השכן של target_word
.
שקול את המשפט הבא בן שמונה מילים:
הדרך הרחבה נצצה בשמש הלוהטת.
מילות ההקשר של כל אחת מ-8 המילים של משפט זה מוגדרות על ידי גודל חלון. גודל החלון קובע את טווח המילים משני הצדדים של 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']
בנה דוגמה אחת לאימון
עבור דילוג-גרם חיובי (target_word, context_word)
, כעת יש לך גם num_ns
מילות הקשר שליליות שנדגמו שאינן מופיעות בשכונת גודל החלון של target_word
. צרף את 1
ההקשר_מילה החיובית context_word
num_ns
ההקשר השליליות לטנזור אחד. זה מייצר קבוצה של דילוגים חיוביים (מסומנים כ 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
אינן חלק ממשפט הקלט. הם שייכים לאוצר המילים כמו מדדים מסוימים אחרים המשמשים בתרשים למעלה.
קומפלו את כל השלבים לפונקציה אחת
טבלת דגימה של דילוג על גרם
מערך נתונים גדול פירושו אוצר מילים גדול יותר עם מספר גבוה יותר של מילים תכופות יותר כגון מילות עצור. דוגמאות הדרכה המתקבלות מדגימת מילים הנפוצות (כגון 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 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
היא כעת רשימה של משפטים מקודדים ב-int. פשוט קרא לפונקציה 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 API כדי להגדיר את מודל 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
את תוצר הנקודות של הטמעות יעד והקשר מזוג אימון. -
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.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 , או מקודד המשפט האוניברסלי הרב-לשוני .
ייתכן שתרצה גם לאמן את המודל על מערך נתונים חדש (קיימים רבים זמינים בערכות נתונים של TensorFlow ).