今天学习使用tensorflow实现word embedding,下面的例子来自于tensorflow的官方文档,实现word embedding本身是比较复杂的,下文只用比较简单的方法实现,限于本人刚入门,水平有限,有些细节还没有彻底搞明白,还需要再花些时间研究,现把今天的研究成果记下供日后继续完善。
文本参考:https://liusida.github.io/2016/11/14/study-embeddings/ 感谢作者的努力与奉献
程序功能:
程序下载一份英文语料库,然后使用word2vec+skip-gram实现word的embedding,最后使用t-SNE选取部分点降维可视化。
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Basic word2vec example."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import collections
import math
import os
import random
import zipfile
import numpy as np
from six.moves import urllib
from six.moves import xrange # pylint: disable=redefined-builtin
import tensorflow as tf
# Step 1: Download the data.
url = 'http://mattmahoney.net/dc/'
def maybe_download(filename, expected_bytes):
"""下载数据集,文件中没有标点,全部小写,词与词之间用空格隔开."""
if not os.path.exists(filename):
filename, _ = urllib.request.urlretrieve(url + filename, filename)
statinfo = os.stat(filename)
if statinfo.st_size == expected_bytes:
print('Found and verified', filename)
else:
print(statinfo.st_size)
raise Exception(
'Failed to verify ' + filename + '. Can you get to it with a browser?')
return filename
filename = maybe_download('text8.zip', 31344016)
# Read the data into a list of strings.
def read_data(filename):
"""Extract the first file enclosed in a zip file as a list of words."""
with zipfile.ZipFile(filename) as f:
data = tf.compat.as_str(f.read(f.namelist()[0])).split()
return data
#读取压缩包中第一个文件的全部内容
vocabulary = read_data(filename)
print('Data size', len(vocabulary))
print ('vocabulary:', vocabulary[:10])
# Step 2: Build the dictionary and replace rare words with UNK token.
vocabulary_size = 50000
def build_dataset(words, n_words):
"""Process raw inputs into a dataset."""
count = [['UNK', -1]]
count.extend(collections.Counter(words).most_common(n_words - 1))
dictionary = dict()
for word, _ in count:
dictionary[word] = len(dictionary)
data = list()
unk_count = 0
for word in words:
if word in dictionary:
index = dictionary[word]
else:
index = 0 # dictionary['UNK']
unk_count += 1
data.append(index)
count[0][1] = unk_count
reversed_dictionary = dict(zip(dictionary.values(), dictionary.keys()))
return data, count, dictionary, reversed_dictionary
#data:把原文中的word转化成ID后的串
#count:[word, freq]存储的是word和word对应的出现次数
#dictionary:词到ID的对应关系
#reverse_dictionary:ID到词的对应关系
data, count, dictionary, reverse_dictionary = build_dataset(vocabulary,
vocabulary_size)
del vocabulary # Hint to reduce memory.
print('Most common words (+UNK)', count[:5])
print('Sample data', data[:10], [reverse_dictionary[i] for i in data[:10]])
data_index = 0
# Step 3: Function to generate a training batch for the skip-gram model.
#batch_size:
#num_skips:
#skip_window:取一个word周边多远的word来训练
def generate_batch(batch_size, num_skips, skip_window):
global data_index
assert batch_size % num_skips == 0
assert num_skips <= 2 * skip_window
batch = np.ndarray(shape=(batch_size), dtype=np.int32)
labels = np.ndarray(shape=(batch_size, 1), dtype=np.int32)
span = 2 * skip_window + 1 # [ skip_window target skip_window ]
buffer = collections.deque(maxlen=span)
if data_index + span > len(data):
data_index = 0
buffer.extend(data[data_index:data_index + span])
data_index += span
for i in range(batch_size // num_skips):
target = skip_window # target label at the center of the buffer
targets_to_avoid = [skip_window]
for j in range(num_skips):
while target in targets_to_avoid:
target = random.randint(0, span - 1)
targets_to_avoid.append(target)
batch[i * num_skips + j] = buffer[skip_window]
labels[i * num_skips + j, 0] = buffer[target]
if data_index == len(data):
buffer[:] = data[:span]
data_index = span
else:
buffer.append(data[data_index])
data_index += 1
# Backtrack a little bit to avoid skipping words in the end of a batch
data_index = (data_index + len(data) - span) % len(data)
return batch, labels
#batch:word对应的ID
#labels:skip-gram算法中word关联的周围的两个word(skip_window=1)
batch, labels = generate_batch(batch_size=8, num_skips=2, skip_window=1)
for i in range(8):
print(batch[i], reverse_dictionary[batch[i]], '->', labels[i, 0], reverse_dictionary[labels[i, 0]])
print (dictionary['a'], dictionary['as'], dictionary['term'])
# Step 4: Build and train a skip-gram model.
batch_size = 128
embedding_size = 128 # Dimension of the embedding vector.
skip_window = 1 # How many words to consider left and right.
num_skips = 2 # How many times to reuse an input to generate a label.
# We pick a random validation set to sample nearest neighbors. Here we limit the
# validation samples to the words that have a low numeric ID, which by
# construction are also the most frequent.
valid_size = 16 # Random set of words to evaluate similarity on.
valid_window = 100 # Only pick dev samples in the head of the distribution.
#从0-99种随机选取16个数
valid_examples = np.random.choice(valid_window, valid_size, replace=False)
num_sampled = 64 # Number of negative examples to sample.
graph = tf.Graph()
with graph.as_default():
# Input data.
train_inputs = tf.placeholder(tf.int32, shape=[batch_size])
train_labels = tf.placeholder(tf.int32, shape=[batch_size, 1])
valid_dataset = tf.constant(valid_examples, dtype=tf.int32)
# Ops and variables pinned to the CPU because of missing GPU implementation
with tf.device('/cpu:0'):
# Look up embeddings for inputs.
embeddings = tf.Variable(tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))
#根据embeddings取出与输入word(train_inputs)对应的128维的向量,内部实现原理稍后再研究
embed = tf.nn.embedding_lookup(embeddings, train_inputs)
# Construct the variables for the NCE loss
nce_weights = tf.Variable(tf.truncated_normal([vocabulary_size, embedding_size], stddev=1.0 / math.sqrt(embedding_size)))
nce_biases = tf.Variable(tf.zeros([vocabulary_size]))
# Compute the average NCE loss for the batch.
# tf.nce_loss automatically draws a new sample of the negative labels each
# time we evaluate the loss.
loss = tf.reduce_mean(
tf.nn.nce_loss(weights=nce_weights,
biases=nce_biases,
labels=train_labels,
inputs=embed,
num_sampled=num_sampled,
num_classes=vocabulary_size))
# Construct the SGD optimizer using a learning rate of 1.0.
optimizer = tf.train.GradientDescentOptimizer(1.0).minimize(loss)
# Compute the cosine similarity between minibatch examples and all embeddings.
# embeddings的二范数,就是向量的长度
norm = tf.sqrt(tf.reduce_sum(tf.square(embeddings), 1, keep_dims=True))
# 对向量标准化成单位向量
normalized_embeddings = embeddings / norm
# 根据normalized_embeddings取出与输入word(valid_dataset)对应的128维的向量
valid_embeddings = tf.nn.embedding_lookup(normalized_embeddings, valid_dataset)
# 计算valid_embeddings和normalized_embeddings的cosine(两个向量都是单位向量)
# 估计是从normalized_embeddings中挑选和valid_embeddings最相似的word
similarity = tf.matmul(valid_embeddings, normalized_embeddings, transpose_b=True)
# Add variable initializer.
init = tf.global_variables_initializer()
# Step 5: Begin training.
num_steps = 40001
with tf.Session(graph=graph) as session:
# We must initialize all variables before we use them.
init.run()
print('Initialized')
average_loss = 0
for step in xrange(num_steps):
batch_inputs, batch_labels = generate_batch(batch_size, num_skips, skip_window)
feed_dict = {train_inputs: batch_inputs, train_labels: batch_labels}
# We perform one update step by evaluating the optimizer op (including it
# in the list of returned values for session.run()
_, loss_val = session.run([optimizer, loss], feed_dict=feed_dict)
average_loss += loss_val
if step % 2000 == 0:
if step > 0:
average_loss /= 2000
# The average loss is an estimate of the loss over the last 2000 batches.
print('Average loss at step ', step, ': ', average_loss)
average_loss = 0
# Note that this is expensive (~20% slowdown if computed every 500 steps)
# 将最相似的八个词输出到屏幕
if step % 10000 == 0:
sim = similarity.eval()
for i in xrange(valid_size):
valid_word = reverse_dictionary[valid_examples[i]]
top_k = 8 # number of nearest neighbors
nearest = (-sim[i, :]).argsort()[1:top_k + 1]
log_str = 'Nearest to %s:' % valid_word
for k in xrange(top_k):
close_word = reverse_dictionary[nearest[k]]
log_str = '%s %s,' % (log_str, close_word)
print(log_str)
final_embeddings = normalized_embeddings.eval()
# Step 6: Visualize the embeddings.
def plot_with_labels(low_dim_embs, labels, filename='tsne.png'):
assert low_dim_embs.shape[0] >= len(labels), 'More labels than embeddings'
plt.figure(figsize=(18, 18)) # in inches
for i, label in enumerate(labels):
x, y = low_dim_embs[i, :]
plt.scatter(x, y)
plt.annotate(label,
xy=(x, y),
xytext=(5, 2),
textcoords='offset points',
ha='right',
va='bottom')
plt.savefig(filename)
try:
# 如果维度过高(比如1024维),建议先使用PCA降维到50维左右,再使用tsne继续降到2到3维
# 因为直接使用tsne效率比较低
# pylint: disable=g-import-not-at-top
from sklearn.manifold import TSNE
import matplotlib.pyplot as plt
# perplexity:一般设置成30即可,这个值一般设置在5到50之间,这个值不少很重要
# n_components:降到2维
# init:可选random或者pca, pca相比random更稳健一些
# n_iter:优化的最大迭代次数,至少要设置到200
# method:可以取两个值:1.barnes_hut(默认):运行速度快,2.exact:运行慢,但精确
tsne = TSNE(perplexity=30, n_components=2, init='pca', n_iter=5000, method='exact')
plot_only = 300 #图上显示点的个数
low_dim_embs = tsne.fit_transform(final_embeddings[:plot_only, :])
# 每个点的label,这里取的是word的名字
labels = [reverse_dictionary[i] for i in xrange(plot_only)]
plot_with_labels(low_dim_embs, labels)
except ImportError:
print('Please install sklearn, matplotlib, and scipy to show embeddings.')
运行结果:
Found and verified text8.zip
Data size 17005207
vocabulary: ['anarchism', 'originated', 'as', 'a', 'term', 'of', 'abuse', 'first', 'used', 'against']
Most common words (+UNK) [['UNK', 418391], ('the', 1061396), ('of', 593677), ('and', 416629), ('one', 411764)]
Sample data [5234, 3084, 12, 6, 195, 2, 3137, 46, 59, 156] ['anarchism', 'originated', 'as', 'a', 'term', 'of', 'abuse', 'first', 'used', 'against']
3084 originated -> 12 as
3084 originated -> 5234 anarchism
12 as -> 6 a
12 as -> 3084 originated
6 a -> 195 term
6 a -> 12 as
195 term -> 6 a
195 term -> 2 of
6 12 195
Initialized
Average loss at step 0 : 303.279144287
Nearest to which: extras, bandits, qua, kilts, kabila, blum, america, benedict,
Nearest to but: colonizing, moseley, raphaelite, probabilistic, mei, evil, anaximenes, peek,
Nearest to been: advocacy, bateson, corpus, capitolina, blasts, prostitution, gillian, adolescence,
Nearest to world: shoshone, secondary, galicia, bengalis, privations, palms, ziegler, misconception,
Nearest to with: argues, bd, nagy, olof, greg, fuego, astros, ltcm,
Nearest to all: herod, lehmann, availability, dogmatic, similarities, vanquished, dagger, sae,
Nearest to so: euboea, abridgment, matriculation, hydrates, graphite, replace, ipcc, nutrients,
Nearest to their: shaking, ankara, savior, luigi, musculoskeletal, retaliation, deity, fhm,
Nearest to people: gasolines, compute, filter, herr, gothic, predicted, hortense, rhetoricians,
Nearest to not: hawthorn, nuggets, alabaster, arian, analysis, imagination, skewed, curtiss,
Nearest to from: instances, tess, bypass, christ, abbe, griswold, reconnaissance, puzzle,
Nearest to up: pir, senator, fades, hungarian, battlecruiser, diogo, anatolian, duplications,
Nearest to in: spy, guoyu, juggalo, wasted, plank, dreadful, smelting, setback,
Nearest to these: reprimanded, withholding, vacations, gru, toile, geneticist, bourke, successfully,
Nearest to one: fortunes, sombre, crispin, independents, electrically, override, hg, defendants,
Nearest to system: sparingly, blish, exchequer, soa, blimp, anhydrous, postscript, mauchly,
Average loss at step 2000 : 113.633764172
Average loss at step 4000 : 52.7582999108
Average loss at step 6000 : 33.5904548576
Average loss at step 8000 : 23.1999923706
Average loss at step 10000 : 17.9230210255
Nearest to which: and, inc, course, var, serve, maintain, one, trench,
Nearest to but: and, aberdeen, var, explicit, probabilistic, moseley, illness, decade,
Nearest to been: advocacy, organizations, bateson, manual, patents, acts, adolescence, his,
Nearest to world: secondary, bckgr, names, placing, sanctioned, major, axiom, anime,
Nearest to with: in, and, of, about, around, by, on, bodies,
Nearest to all: similarities, ontario, learn, defendants, two, albania, restless, algorithms,
Nearest to so: leading, euboea, nutrients, zeus, appendages, canada, indicated, afghanistan,
Nearest to their: ankara, deity, bank, agave, tuna, the, ngc, ads,
Nearest to people: compute, predicted, gothic, maintaining, four, ian, transcontinental, push,
Nearest to not: portuguese, dominion, analysis, hawthorn, org, online, space, persecute,
Nearest to from: in, and, of, as, instances, walls, ltd, by,
Nearest to up: senator, hungarian, anatolian, socioeconomic, heuvelmans, economists, prize, fades,
Nearest to in: and, of, by, on, with, nine, at, from,
Nearest to these: successfully, bckgr, hilly, markov, sub, commands, vacations, unaids,
Nearest to one: two, nine, bckgr, molinari, zero, UNK, five, archie,
Nearest to system: holding, cia, hooker, moon, polynomial, center, antisemitism, sleep,
Average loss at step 12000 : 14.0672037274
Average loss at step 14000 : 11.8982646112
Average loss at step 16000 : 9.82810585904
Average loss at step 18000 : 8.32750001115
Average loss at step 20000 : 8.05992223346
Nearest to which: and, that, inc, dtp, salmon, course, many, agouti,
Nearest to but: and, suebi, thoreau, dtp, dye, var, however, agouti,
Nearest to been: advocacy, prostitution, had, by, as, krebs, would, patents,
Nearest to world: secondary, bckgr, names, placing, operatorname, palms, galicia, axiom,
Nearest to with: in, and, for, of, by, about, from, as,
Nearest to all: similarities, algorithms, ontario, learn, the, agouti, defendants, dogmatic,
Nearest to so: euboea, nutrients, aleksander, leading, graphite, zeus, appendages, helsinki,
Nearest to their: his, the, ankara, dasyprocta, agave, tuna, deity, bank,
Nearest to people: compute, four, gothic, predicted, dasyprocta, dtp, maintaining, agouti,
Nearest to not: it, dominion, alabaster, they, portuguese, analysis, circ, agouti,
Nearest to from: in, and, by, with, subkey, on, nine, to,
Nearest to up: senator, hungarian, anatolian, arrogant, localities, vessels, latinus, prize,
Nearest to in: and, at, of, with, on, from, for, nine,
Nearest to these: the, successfully, sanskrit, bats, some, mathrm, bckgr, acacia,
Nearest to one: two, three, six, seven, nine, agouti, eight, dasyprocta,
Nearest to system: hbox, hepburn, postscript, mauchly, holding, alem, cia, polynomial,
Average loss at step 22000 : 7.00414496851
Average loss at step 24000 : 6.88763291979
Average loss at step 26000 : 6.72134333861
Average loss at step 28000 : 6.38215904641
Average loss at step 30000 : 5.92981643927
Nearest to which: that, this, and, it, agouti, one, dtp, inc,
Nearest to but: and, however, suebi, agouti, when, dye, thoreau, var,
Nearest to been: advocacy, prostitution, be, had, by, was, krebs, patents,
Nearest to world: trinomial, secondary, bckgr, names, placing, sponsors, axiom, ziegler,
Nearest to with: in, and, mishnayot, by, from, for, around, six,
Nearest to all: similarities, algorithms, many, agouti, availability, dogmatic, faster, archie,
Nearest to so: euboea, hopwood, crandall, helsinki, appendages, leading, nutrients, aleksander,
Nearest to their: his, the, its, ankara, dasyprocta, ngc, a, tuna,
Nearest to people: compute, gothic, predicted, dtp, push, dasyprocta, maintaining, paired,
Nearest to not: it, they, dominion, also, circ, to, agouti, vaccines,
Nearest to from: in, by, with, and, of, to, nine, subkey,
Nearest to up: hungarian, senator, anatolian, lossy, pir, arrogant, vessels, localities,
Nearest to in: at, and, on, with, of, from, by, for,
Nearest to these: the, some, successfully, spines, sanskrit, many, bats, reprimanded,
Nearest to one: two, seven, eight, four, agouti, six, dasyprocta, circ,
Nearest to system: hbox, hepburn, postscript, alem, cia, mauchly, holding, hooker,
Average loss at step 32000 : 5.96737474769
Average loss at step 34000 : 5.70978498816
Average loss at step 36000 : 5.78236852288
Average loss at step 38000 : 5.49960520446
Average loss at step 40000 : 5.25752883768
Nearest to which: that, this, and, it, agouti, one, also, who,
Nearest to but: and, however, suebi, when, or, agouti, dye, thoreau,
Nearest to been: be, advocacy, had, prostitution, was, by, organizations, were,
Nearest to world: trinomial, secondary, bckgr, placing, names, axiom, status, weston,
Nearest to with: in, mishnayot, around, and, from, between, by, as,
Nearest to all: many, similarities, algorithms, agouti, orion, availability, manipulating, archie,
Nearest to so: euboea, crandall, hopwood, helsinki, appendages, maidens, leading, nutrients,
Nearest to their: his, its, the, vma, dasyprocta, ankara, ngc, some,
Nearest to people: compute, gasolines, gothic, predicted, dtp, push, paired, maintaining,
Nearest to not: it, they, dominion, also, there, vaccines, circ, to,
Nearest to from: in, and, on, with, of, subkey, nine, by,
Nearest to up: senator, hungarian, lossy, arrogant, anatolian, localities, pir, fades,
Nearest to in: at, and, on, sponsors, from, nine, zero, with,
Nearest to these: some, many, spines, the, successfully, bats, both, three,
Nearest to one: two, four, six, three, zero, eight, seven, agouti,
Nearest to system: hepburn, hbox, alem, cia, postscript, holding, hooker, mauchly,