کمک به حفاظت از دیواره بزرگ مرجانی با TensorFlow در Kaggle اضافه کردن چالش

Word2Vec

مشاهده در TensorFlow.org در Google Colab اجرا شود مشاهده منبع در GitHub دانلود دفترچه یادداشت

Word2Vec یک الگوریتم منفرد نیست، بلکه خانواده ای از معماری ها و بهینه سازی های مدل است که می تواند برای یادگیری جاسازی کلمات از مجموعه داده های بزرگ استفاده شود. جاسازی‌هایی که از طریق Word2Vec آموخته شده‌اند در انواع وظایف پردازش زبان طبیعی پایین دستی موفق هستند.

این مقالات دو روش را برای یادگیری بازنمایی کلمات پیشنهاد کردند:

  • مدل کیسه کلمات پیوسته که کلمه میانی را بر اساس کلمات بافت اطراف پیش‌بینی می‌کند. متن از چند کلمه قبل و بعد از کلمه فعلی (وسط) تشکیل شده است. این معماری را مدل کیسه ای از کلمات می نامند زیرا ترتیب کلمات در متن مهم نیست.
  • مدل Skip-gram Continuous که کلمات را در محدوده معینی قبل و بعد از کلمه فعلی در همان جمله پیش بینی می کند. نمونه کار شده آن در زیر آورده شده است.

در این آموزش از روش skip-gram استفاده خواهید کرد. ابتدا، skip-grams و سایر مفاهیم را با استفاده از یک جمله برای مثال بررسی خواهید کرد. در مرحله بعد، مدل Word2Vec خود را بر روی یک مجموعه داده کوچک آموزش خواهید داد. این آموزش همچنین حاوی کدهایی برای صادرات تعبیه‌های آموزش‌دیده و تجسم آنها در پروژکتور جاسازی TensorFlow است.

نمونه برداری گرم و منفی

در حالی که یک مدل کیسه‌ای از کلمات، یک کلمه را با توجه به بافت همسایه پیش‌بینی می‌کند، یک مدل skip-gram بافت (یا همسایگان) یک کلمه را با توجه به خود کلمه پیش‌بینی می‌کند. این مدل بر روی skip-gram ها آموزش داده شده است، که n-gram هستند که اجازه می دهند توکن ها رد شوند (نمونه نمودار زیر را ببینید). متن یک کلمه را می توان از طریق مجموعه ای از جفت های skip-gram از (target_word, context_word) که در آن context_word در زمینه همسایه target_word ظاهر می شود، نمایش داده می شود.

جمله 8 کلمه ای زیر را در نظر بگیرید.

جاده عریض زیر آفتاب داغ می درخشید.

کلمات متنی برای هر یک از 8 کلمه این جمله با اندازه پنجره تعریف می شوند. اندازه پنجره دامنه کلمات را در دو طرف target_word می کند که می تواند context word در نظر گرفته شود. به این جدول پرش گرم برای کلمات هدف بر اساس اندازه های مختلف پنجره نگاه کنید.

word2vec_skipgrams

هدف آموزشی مدل پرش گرم، به حداکثر رساندن احتمال پیش‌بینی کلمات متنی با توجه به کلمه هدف است. برای دنباله ای از کلمات w 1 , w 2 , ... w T , هدف را می توان به صورت میانگین احتمال log نوشت

word2vec_skipgram_objective

که در آن c اندازه زمینه آموزشی است. فرمول اولیه skip-gram این احتمال را با استفاده از تابع softmax تعریف می کند.

word2vec_full_softmax

که در آن v و v ' بازنمایی بردار هدف و زمینه کلمات هستند و W اندازه واژگان است.

محاسبه مخرج این فرمول شامل انجام یک نرم افزار کامل بر روی کل کلمات واژگانی است که اغلب عبارت های بزرگ (10 5 - 10 7 ) است.

تابع تلفات تخمین تقابل نویز یک تقریب کارآمد برای یک Softmax کامل است. با هدف یادگیری جاسازی کلمه به جای مدل سازی توزیع کلمه، از دست دادن NCE می تواند برای استفاده از نمونه گیری منفی ساده شود.

هدف نمونه گیری منفی ساده شده برای یک کلمه هدف، تمایز کلمه متن از num_ns نمونه های منفی است که از توزیع نویز Pn ( w) کلمات گرفته شده است. به‌طور دقیق‌تر، یک تقریب کارآمد از softmax کامل بر روی واژگان، برای یک جفت پرش-گرم، این است که از دست دادن یک کلمه هدف را به عنوان یک مشکل طبقه‌بندی بین کلمه متن و num_ns نمونه‌های منفی مطرح کند.

یک نمونه منفی به عنوان یک جفت (target_word، context_word) تعریف می شود به طوری که context_word در همسایگی 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 برای تولید جفت‌های skip-gram از 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 نمونه های منفی برای کلمه مورد نظر در یک پنجره استفاده کنید. می‌توانید تابع را روی کلمه هدف یک skip-grams فراخوانی کنید و کلمه متنی را به عنوان کلاس واقعی ارسال کنید تا از نمونه برداری حذف شود.

# 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 ظاهر نمی شوند. 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)

نگاهی به متن و برچسب های مربوط به کلمه مورد نظر از مثال skip-gram بالا بیندازید.

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_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] نشان دهنده احتمال نمونه برداری از 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 برای تولید بردار برای هر عنصر در 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 است. برای تولید نمونه های آموزشی برای مدل Word2Vec کافیست تابع generate_training_data() genere_training_data را که قبلا تعریف شده بود فراخوانی کنید. به طور خلاصه، تابع روی هر کلمه از هر دنباله تکرار می شود تا کلمات متن مثبت و منفی را جمع آوری کند. طول هدف، زمینه‌ها و برچسب‌ها باید یکسان باشد و تعداد کل نمونه‌های آموزشی را نشان دهد.

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 را می توان به عنوان یک طبقه بندی برای تمایز بین کلمات بافت واقعی از skip-gram و کلمات متن نادرست که از طریق نمونه گیری منفی به دست می آید، پیاده سازی کرد. شما می توانید یک محصول نقطه ای بین جاسازی کلمات هدف و متن برای به دست آوردن پیش بینی برچسب ها و محاسبه ضرر در برابر برچسب های واقعی در مجموعه داده انجام دهید.

مدل زیر کلاس 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 که حاصل ضرب نقطه جاسازی های هدف و زمینه را از یک جفت آموزشی محاسبه می کند.
  • flatten : یک tf.keras.layers.Flatten یک لایه برای مسطح کردن نتایج لایه dots به logits.

با مدل زیر کلاس، می‌توانید تابع call() را تعریف کنید که جفت‌های (target, context) را می‌پذیرد که سپس می‌توانند به لایه تعبیه‌شده مربوطه منتقل شوند. برای اجرای یک محصول نقطه ای با target_embedding ، context_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'])

همچنین یک callback برای ثبت آمار آموزش برای تانسوربرد تعریف کنید.

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 skip-gram را با نمونه گیری منفی از ابتدا پیاده سازی کنید و جاسازی های کلمه به دست آمده را تجسم کنید.