질문이있다? TensorFlow 포럼 방문 포럼 에서 커뮤니티와 연결

Word2Vec

TensorFlow.org에서보기 Google Colab에서 실행 GitHub에서 소스보기노트북 다운로드

Word2Vec은 단일 알고리즘이 아니라 대규모 데이터 세트에서 단어 임베딩을 학습하는 데 사용할 수있는 모델 아키텍처 및 최적화 제품군입니다. Word2Vec을 통해 학습 한 임베딩은 다양한 다운 스트림 자연어 처리 작업에서 성공적인 것으로 입증되었습니다.

이 논문은 단어 표현을 학습하기위한 두 가지 방법을 제안했습니다.

  • 주변 문맥 단어를 기반으로 중간 단어를 예측하는 연속 Bag-of-Words 모델 . 컨텍스트는 현재 (중간) 단어 앞뒤에있는 몇 개의 단어로 구성됩니다. 이 아키텍처는 문맥에서 단어의 순서가 중요하지 않기 때문에 bag-of-words 모델이라고합니다.
  • 같은 문장에서 현재 단어의 앞뒤로 일정 범위 내의 단어를 예측하는 연속 스킵 그램 모델 . 이에 대한 실제 예제가 아래에 나와 있습니다.

이 자습서에서는 스킵 그램 접근 방식을 사용합니다. 먼저, 설명을 위해 단일 문장을 사용하여 스킵 그램 및 기타 개념을 탐색합니다. 다음으로 작은 데이터 세트에서 자체 Word2Vec 모델을 학습합니다. 이 가이드에는 학습 된 임베딩을 내보내고 TensorFlow Embedding Projector 에서 시각화하는 코드도 포함되어 있습니다.

스킵 그램 및 네거티브 샘플링

bag-of-words 모델은 주변 문맥이 주어지면 단어를 예측하는 반면, skip-gram 모델은 단어 자체가 주어지면 단어의 문맥 (또는 이웃)을 예측합니다. 모델은 토큰을 건너 뛸 수있는 n-gram 인 skip-gram에 대해 학습됩니다 (예는 아래 다이어그램 참조). 단어의 컨텍스트는 (target_word, context_word) 의 스킵 그램 쌍 세트를 통해 표현 될 수 있으며 (target_word, context_word) 여기서 context_wordtarget_word 의 인접 컨텍스트에 나타납니다.

8 단어의 다음 문장을 고려하십시오.

넓은 길은 뜨거운 햇살에 반짝였다.

이 문장의 8 개 단어 각각에 대한 문맥 단어는 창 크기로 정의됩니다. 창 크기는 context word 로 간주 될 수있는 target_word 양쪽에있는 단어의 범위를 결정합니다. 다른 창 크기에 따라 대상 단어에 대한이 스킵 그램 표를 살펴보십시오.

word2vec_skipgrams

스킵 그램 모델의 훈련 목표는 대상 단어가 주어지면 문맥 단어를 예측할 확률을 최대화하는 것입니다. 단어 w 1 , w 2 , ... w T 의 경우 목표는 평균 로그 확률로 작성 될 수 있습니다.

word2vec_skipgram_objective

여기서 c 는 훈련 컨텍스트의 크기입니다. 기본 스킵 그램 공식은 softmax 함수를 사용하여이 확률을 정의합니다.

word2vec_full_softmax

여기서 vv ' 는 단어의 대상 및 컨텍스트 벡터 표현이고 W 는 어휘 크기입니다.

이 공식의 분모를 계산하는 것은 종종 큰 (10 5 -10 7 ) 용어 인 전체 어휘 단어에 대해 완전한 소프트 맥스를 수행하는 것을 포함합니다.

Noise Contrastive Estimation loss 함수는 전체 소프트 맥스에 대한 효율적인 근사치입니다. 단어 분포를 모델링하는 대신 단어 임베딩을 배우는 목적으로 NCE 손실을 단순화 하여 음수 샘플링을 사용할 수 있습니다.

대상 단어에 대한 단순화 된 네거티브 샘플링 목적은 단어의 노이즈 분포 P n (w) 에서 가져온 num_ns 네거티브 샘플과 컨텍스트 단어를 구별하는 것입니다. 보다 정확하게는 스킵 그램 쌍의 경우 어휘에 대한 전체 소프트 맥스의 효율적인 근사값 은 문맥 단어와 num_ns 음성 샘플 사이의 분류 문제로 대상 단어에 대한 손실을 제기하는 것입니다.

네거티브 샘플은 (target_word, context_word) 쌍으로 정의되어 context_word가 target_word의 window_size 근처에 나타나지 않습니다. 예제 문장의 경우 잠재적 인 음수 샘플이 거의 없습니다 ( window_size 가 2 일 때).

(hot, shimmered)
(wide, hot)
(wide, sun)

다음 섹션에서는 단일 문장에 대한 스킵 그램과 부정적인 샘플을 생성합니다. 또한 자습서 뒷부분에서 서브 샘플링 기술에 대해 배우고 양성 및 음성 학습 예제에 대한 분류 모델을 학습합니다.

설정

import io
import re
import string
import tensorflow as tf
import tqdm

from tensorflow.keras import Model
from tensorflow.keras.layers import Dot, Embedding, Flatten
from tensorflow.keras.layers.experimental.preprocessing import TextVectorization
# 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 모듈은 tf.keras.preprocessing.sequence 데이터 준비를 단순화하는 유용한 기능을 제공합니다. tf.keras.preprocessing.sequence.skipgrams 를 사용하여 [0, vocab_size) 범위의 토큰에서 주어진 window_size example_sequence 에서 스킵 그램 쌍을 생성 할 수 있습니다.

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

스킵 그램 1 개에 대한 음성 샘플링

skipgrams 함수는 주어진 창 범위 위로 슬라이드하여 모든 양의 스킵 그램 쌍을 반환합니다. 학습을위한 음성 샘플로 사용할 추가 스킵 그램 쌍을 생성하려면 어휘에서 임의의 단어를 샘플링해야합니다. tf.random.log_uniform_candidate_sampler 함수를 사용하여 창에서 주어진 대상 단어에 대해 num_ns 개의 음수 샘플을 샘플링합니다. 스킵 그램의 대상 단어에 대해 함수를 호출하고 컨텍스트 단어를 true 클래스로 전달하여 샘플링에서 제외 할 수 있습니다.

# 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) skip-gram에 대해 이제 target_word 의 창 크기 근처에 나타나지 않는 num_ns 부정적인 샘플링 컨텍스트 단어가 있습니다. 1 긍정적 인 context_wordnum_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    : 1
target_word     : the
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(1, 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 )를 샘플링하여 얻은 학습 예제는 모델이 학습 할 유용한 정보를 많이 추가하지 않습니다. Mikolov et al. 임베딩 품질을 개선하는 데 유용한 방법으로 자주 사용되는 단어의 서브 샘플링을 제안합니다.

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

파일에서 텍스트를 읽고 처음 몇 줄을 살펴 봅니다.

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 레이어를 사용하여 말뭉치에서 문장을 벡터화 할 수 있습니다. 이 텍스트 분류 자습서에서이 레이어 사용에 대해 자세히 알아보십시오. 위의 처음 몇 문장에서 텍스트는 한 대문자 여야하고 구두점은 제거해야합니다. 이를 위해 TextVectorization 레이어에서 사용할 수있는 custom_standardization function 를 정의합니다.

# 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 text vectorization layer to normalize, split, and map strings to
# integers. Set output_sequence_length length to pad all samples to same length.
vectorize_layer = 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 인코딩 된 문장의 목록입니다. Word2Vec 모델에 대한 학습 예제를 생성하려면 앞서 정의한 generate_training_data() 함수를 호출하기 만하면됩니다. 요약하자면이 함수는 각 시퀀스의 각 단어를 반복하여 긍정적이고 부정적인 컨텍스트 단어를 수집합니다. 대상, 컨텍스트 및 레이블의 길이는 동일해야하며 총 학습 예제 수를 나타냅니다.

targets, contexts, labels = generate_training_data(
    sequences=sequences,
    window_size=2,
    num_ns=4,
    vocab_size=vocab_size,
    seed=SEED)
print(len(targets), len(contexts), len(labels))
100%|██████████| 32777/32777 [00:25<00:00, 1267.26it/s]
64720 64720 64720

성능을위한 데이터 세트 구성

잠재적으로 많은 수의 학습 예제에 대해 효율적인 일괄 처리를 수행하려면tf.data.Dataset API를 사용하세요. 이 단계 후에는 (target_word, context_word), (label) 요소의tf.data.Dataset 개체를 사용하여 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, 1)), (1024, 5)), types: ((tf.int32, tf.int64), tf.int64)>

성능을 향상시키기 위해 cache()prefetch() 를 추가하십시오.

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

모델 및 훈련

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 레이어입니다.
  • flatten : dots 레이어의 결과를 tf.keras.layers.Flatten 으로 병합하는 tf.keras.layers.Flatten 레이어입니다.

서브 클래 싱 된 모델을 사용하면 (target, context) 쌍을 허용하는 call() 함수를 정의 할 수 있으며 (target, context) 이를 해당 임베딩 레이어로 전달할 수 있습니다. context_embedding 를 변경하여 target_embedding 으로 내적을 수행하고 평면화 된 결과를 반환합니다.

class Word2Vec(Model):
  def __init__(self, vocab_size, embedding_dim):
    super(Word2Vec, self).__init__()
    self.target_embedding = Embedding(vocab_size,
                                      embedding_dim,
                                      input_length=1,
                                      name="w2v_embedding")
    self.context_embedding = Embedding(vocab_size,
                                       embedding_dim,
                                       input_length=num_ns+1)
    self.dots = Dot(axes=(3, 2))
    self.flatten = Flatten()

  def call(self, pair):
    target, context = pair
    word_emb = self.target_embedding(target)
    context_emb = self.context_embedding(context)
    dots = self.dots([context_emb, word_emb])
    return self.flatten(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)

모델을 만들 시간입니다! 128의 임베딩 차원으로 Word2Vec 클래스를 인스턴스화하십시오 (다른 값으로 실험 할 수 있음). 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_callback = tf.keras.callbacks.TensorBoard(log_dir="logs")

일부 Epoch 동안 위에 준비된 dataset 모델을 훈련시킵니다.

word2vec.fit(dataset, epochs=20, callbacks=[tensorboard_callback])
Epoch 1/20
63/63 [==============================] - 2s 11ms/step - loss: 1.6082 - accuracy: 0.2308
Epoch 2/20
63/63 [==============================] - 0s 5ms/step - loss: 1.5889 - accuracy: 0.5539
Epoch 3/20
63/63 [==============================] - 0s 5ms/step - loss: 1.5412 - accuracy: 0.6001
Epoch 4/20
63/63 [==============================] - 0s 5ms/step - loss: 1.4585 - accuracy: 0.5751
Epoch 5/20
63/63 [==============================] - 0s 5ms/step - loss: 1.3600 - accuracy: 0.5834
Epoch 6/20
63/63 [==============================] - 0s 5ms/step - loss: 1.2628 - accuracy: 0.6110
Epoch 7/20
63/63 [==============================] - 0s 5ms/step - loss: 1.1719 - accuracy: 0.6456
Epoch 8/20
63/63 [==============================] - 0s 5ms/step - loss: 1.0875 - accuracy: 0.6791
Epoch 9/20
63/63 [==============================] - 0s 5ms/step - loss: 1.0092 - accuracy: 0.7101
Epoch 10/20
63/63 [==============================] - 0s 5ms/step - loss: 0.9366 - accuracy: 0.7379
Epoch 11/20
63/63 [==============================] - 0s 5ms/step - loss: 0.8695 - accuracy: 0.7622
Epoch 12/20
63/63 [==============================] - 0s 5ms/step - loss: 0.8076 - accuracy: 0.7848
Epoch 13/20
63/63 [==============================] - 0s 5ms/step - loss: 0.7507 - accuracy: 0.8050
Epoch 14/20
63/63 [==============================] - 0s 5ms/step - loss: 0.6985 - accuracy: 0.8217
Epoch 15/20
63/63 [==============================] - 0s 5ms/step - loss: 0.6507 - accuracy: 0.8368
Epoch 16/20
63/63 [==============================] - 0s 5ms/step - loss: 0.6071 - accuracy: 0.8506
Epoch 17/20
63/63 [==============================] - 0s 5ms/step - loss: 0.5673 - accuracy: 0.8633
Epoch 18/20
63/63 [==============================] - 0s 5ms/step - loss: 0.5311 - accuracy: 0.8741
Epoch 19/20
63/63 [==============================] - 0s 5ms/step - loss: 0.4981 - accuracy: 0.8842
Epoch 20/20
63/63 [==============================] - 0s 5ms/step - loss: 0.4681 - accuracy: 0.8936
<tensorflow.python.keras.callbacks.History at 0x7f120c5b5210>

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.tsvmetadata.tsv 를 다운로드하여 Embedding Projector 에서 얻은 임베딩을 분석합니다.

try:
  from google.colab import files
  files.download('vectors.tsv')
  files.download('metadata.tsv')
except Exception:
  pass

다음 단계

이 튜토리얼에서는 처음부터 네거티브 샘플링을 사용하여 스킵 그램 Word2Vec 모델을 구현하고 얻은 단어 임베딩을 시각화하는 방법을 보여주었습니다.