Word Embeddings
Summary
from datetime import datetime
import io
import os
import re
import shutil
import string
from absl import logging
import numpy as np
import tensorflow as tf
logging.set_verbosity(1)
IMDB_URL = "https://ai.stanford.edu/~amaas/data/sentiment/aclImdb_v1.tar.gz"
def download_imdb(url=IMDB_URL, base_dir=base_dir):
dataset = tf.keras.utils.get_file(os.path.basename(url),
url,
untar=True,
cache_dir=base_dir,
cache_subdir='')
dataset_dir = os.path.join(os.path.dirname(dataset), 'aclImdb')
logging.info('%s', os.listdir(dataset_dir))
return dataset_dir
if os.path.isdir(os.path.join(base_dir, 'aclImdb', 'train')):
dataset_dir = os.path.join(base_dir, 'aclImdb')
else:
dataset_dir = download_imdb(IMDB_URL)
tf.keras.utils.get_file
API link: https://www.tensorflow.org/api_docs/python/tf/keras/utils/get_file
cache_dir 에 파일이 없다면 다운로드
이 중 train/unsup 은 필요 없으니 삭제 (unsupervised dataset)
unsup_dir = os.path.join(dataset_dir, 'train', 'unsup')
![ -d "{unsup_dir}" ] && rm -rf "{unsup_dir}"
!ls "{dataset_dir}/train"
import glob
import pandas as pd
data_dirs = ['train/pos', 'train/neg', 'test/pos', 'test/neg']
data_list = [os.path.join(dataset_dir, path) for path in data_dirs]
data_list = [glob.glob(os.path.join(path, '*')) for path in data_list]
headers = [path.split('/') for path in data_dirs] # make multi-level index for Pandas
headers = pd.MultiIndex.from_tuples(headers)
print(headers)
data_list = pd.DataFrame(data_list, index=headers)
print(data_list)
assert not os.path.isdir(os.path.join(dataset_dir, 'train', 'unsup'))
print('-' * 50)
print(f'num of data for each split: {data_list.shape[1]}')
print(f'total data: {data_list.shape[0] * data_list.shape[1]}')
def make_dataset(path, seed=0, batch_size=1024, validation_split=0.2):
logging.debug('path: %s, seed: %d, batch_size: %d, validation_split: %f',
path, seed, batch_size, validation_split)
train_ds = tf.keras.preprocessing.text_dataset_from_directory(
path,
batch_size=batch_size,
validation_split=validation_split,
subset='training',
seed=seed)
val_ds = tf.keras.preprocessing.text_dataset_from_directory(
path,
batch_size=batch_size,
validation_split=validation_split,
subset='validation',
seed=seed)
return train_ds, val_ds
train_ds, val_ds = make_dataset(os.path.join(dataset_dir, 'train'), seed=123)
for text_batch, label_batch in train_ds.take(1):
for i in range(5):
print(label_batch[i].numpy(), text_batch.numpy()[i])
Dataset 설정
가속을 위해 Dataset 에 설정을 추가함
cache(): 데이터를 메모리에 올림. 데이터가 메모리 크기에 비해 크다면, on-disk 캐쉬를 생성할 수 있음. 이는 작은 여러개의 파일보다 성능이 높음.
prefetch(): 학습 동안 데이터 전처리와 모델 실행을 overlap 함.
data performance 디스크 캐쉬 등 여러 방법을 설명함
AUTOTUNE = tf.data.experimental.AUTOTUNE
train_ds = train_ds.cache().prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)
Embedding layer
임베딩 레이어는 입력으로 integer 인덱스를 받아서, 이에 해당하는 실수 vector 를 출력으로 반환하는 lookup table 으로 볼 수 있다.
여기서 임베딩의 크기는 하이퍼파라미터로 실험을 통해 조절이 필요하다.
tf.keras.layers.Embedding
tf.keras.layers.Embedding(
input_dim, output_dim, embeddings_initializer='uniform',
embeddings_regularizer=None, activity_regularizer=None,
embeddings_constraint=None, mask_zero=False, input_length=None, **kwargs
)
EMBEDDING_SIZE = 5
embedding_layer = tf.keras.layers.Embedding(1000, EMBEDDING_SIZE)
임베딩 레이어를 생성하면 처음에 랜덤한 값으로 초기화 되고, 이후에는 backpropagation 을 통해 점차적으로 조절된다. 학습이 완료되면, 임베딩은 단어 사이의 유사도 를 인코딩한다.
result = embedding_layer(tf.constant([1,2,3]))
result.numpy()
임베딩 레이어는 입력된 각 value 를 이에 해당하는 embedding 으로 변환하므로, 한 차원이 추가된다.
임베딩 사이즈가 $N$ 이라고 했을 때, \ $(I, J, K)$ 차원의 입력이 들어오면 $(I, J, K, N)$ 차원이 출력된다.
input = np.random.randint(6, size=(2, 3))
input = tf.convert_to_tensor(input, dtype=tf.int32)
embeddings = embedding_layer(input)
assert embeddings.shape == list(input.shape) + [EMBEDDING_SIZE]
print(embeddings.shape)
variable length 를 입력으로 받기 위해서는 여러가지 표준화된 방법들이 있다.
- RNN
- Attention
- pooling layer
- Padding + cropping
여기서는 Pooling Layer 방법을 사용한다.
Model
본 예제에서는 단순한 형태의 모델을 사용한다.
모델의 구조는 아래와 같다.
input: batch of string sentence (batch, -1)
- Text preprocessing layer:
(batch, sequence_length, 1)- standardize
- padding/cropping 수행
- Embedding layer:
(batch, sequence_length, embedding_dim)- positive integer 입력값을 encoding 된 임베딩 벡터 값으로 변환
- Global 1D pooling layer:
(batch, embedding_dim)- sequence 차원으로 평균을 취함으로써 fixed-length 벡터를 만듦
- Fully connected layer:
(batch, num_of_fc) - Output layer:
(batch, 1)
def custom_standardization(input_data):
lowercase = tf.strings.lower(input_data)
stripped_html = tf.strings.regex_replace(lowercase, '<br />', ' ')
return tf.strings.regex_replace(stripped_html,
'[%s]' % re.escape(string.punctuation),
'')
# Vocabulary size and number of words in a sequence.
vocab_size = 10000
sequence_length = 100
# Use the text vectorization layer to normalize, split, and map strings to
# integers. Note that the layer uses the custom standardization defined above.
# Set maximum_sequence length as all samples are not of the same length.
vectorize_layer = tf.keras.layers.experimental.preprocessing.TextVectorization(
standardize=custom_standardization,
max_tokens=vocab_size,
output_mode='int',
output_sequence_length=sequence_length)
# Make a text-only dataset (no labels) and call adapt to build the vocabulary.
text_ds = train_ds.map(lambda x, y: x)
vectorize_layer.adapt(text_ds)
embedding_dim=16
model = tf.keras.Sequential([
vectorize_layer,
tf.keras.layers.Embedding(vocab_size, embedding_dim, name="embedding"),
tf.keras.layers.GlobalAveragePooling1D(),
tf.keras.layers.Dense(16, activation='relu'),
tf.keras.layers.Dense(1)
])
tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir="logs")
Compile 하고 학습 진행
model.compile(optimizer='adam',
loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
metrics=['accuracy'])
model.fit(
train_ds,
validation_data=val_ds,
epochs=15,
callbacks=[tensorboard_callback])
모델 구조는 아래와 같다.
model.summary()
학습 진행은 아래와 같다.
%load_ext tensorboard
%tensorboard --logdir logs
(loss 감소를 보니 좀 더 진행해도 괜찮았을듯)
학습된 Embedding 추출 및 저장
상기와 같이 학습된 embedding 을 추출하고 저장하여 추후에 사용하도록 한다.
embedding 의 구조는 (vocab_size, embedding_dimension) 의 형태를 가진다.
embedding layer 의 weight 값을 가져오는 방법은 get_layer() 와 get_weights() 를 사용하는 것이다.
그리고, TextVectorization 레이어 클래스의 get_vocabulary() 함수는 vocabulary 의 각 token 에 대한 meta 정보를 가져온다.
def get_embedding(model,
embedding_layer_name='embedding',
vectorize_layer_name='text_vectorization'):
weights = model.get_layer(embedding_layer_name).get_weights()[0]
meta = model.get_layer(vectorize_layer_name).get_vocabulary()
return weights, meta
weights, vocab = get_embedding(model, vectorize_layer_name=vectorize_layer.name)
추출한 embedding 을 저장하기 위해서는 Embedding Projector 를 사용한다.
상기 추출한 weight 와 meta 정보를 tab separated format 으로 저장하여 업로드하면 된다.
def save_embedding(weights, meta, log_dir='.'):
out_v = io.open(os.path.join(log_dir, 'vectors.tsv'), 'w', encoding='utf-8')
out_m = io.open(os.path.join(log_dir, 'metadata.tsv'), 'w', encoding='utf-8')
for index, word in enumerate(meta):
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()
save_embedding(weights, vocab)
Embedding Projector in Colab
https://www.tensorflow.org/tensorboard/tensorboard_projector_plugin 에서 보면 바로 Colab tensorboard 상에서 embedding 을 visualize 할 수 있다.
log_dir='/logs/imdb-example/'
if not os.path.exists(log_dir):
os.makedirs(log_dir)
save_embedding(weights, vocab, log_dir=log_dir)
여기서는 weights 를 checkpoint 로 저장한다.
weights = tf.Variable(weights)
# Create a checkpoint from embedding, the filename and key are
# name of the tensor.
checkpoint = tf.train.Checkpoint(embedding=weights)
checkpoint.save(os.path.join(log_dir, "embedding.ckpt"))
!ls /logs/imdb-example
새로 configuration
from tensorboard.plugins import projector
# Set up config
config = projector.ProjectorConfig()
embedding = config.embeddings.add()
# The name of the tensor will be suffixed by `/.ATTRIBUTES/VARIABLE_VALUE`
embedding.tensor_name = "embedding/.ATTRIBUTES/VARIABLE_VALUE"
embedding.metadata_path = 'metadata.tsv'
projector.visualize_embeddings(log_dir, config)
Tensorboard로 visualize
%tensorboard --logdir /logs/imdb-example/

