text: 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
  import tensorflow as tf
  
  # 第一步: 在下面这个地址下载语料库
  url = 'http://mattmahoney.net/dc/'
  
  
  def data_path(file_name):
      current_dir = os.path.dirname(__file__)
      parent_dir = os.path.abspath(os.path.join(current_dir, os.pardir, os.pardir))
      data_dir = os.path.join(parent_dir, "data_source", file_name)
      return data_dir
  
  
  def maybe_download(filename, expected_bytes):
      """
      这个函数的功能是：
      如果filename不存在，就在上面的地址下载它。
      如果filename存在，就跳过下载。
      最终会检查文字的字节数是否和expected_bytes相同。
      """
      # if not os.path.exists(filename):
      if not os.path.exists(data_path(filename)):
          print('start downloading...')
          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
  
  
  # 下载语料库text8.zip并验证下载
  # filename = maybe_download('text8.zip', 31344016)
  # print(filename)
  # print("%%%%%%%%%%%%%%%%%")
  
  filename = data_path("text8.zip")
  print(filename)
  
  
  # 将语料库解压，并转换成一个word的list
  def read_data(filename):
      """
      这个函数的功能是：
      将下载好的zip文件解压并读取为word的list
      """
      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))  # 总长度为1700万左右
  # 输出前100个词。
  print(vocabulary[0:100])
  
  # 第二步: 制作一个词表，将不常见的词变成一个UNK标识符
  # 词表的大小为5万（即我们只考虑最常出现的5万个词）
  vocabulary_size = 50000
  
  
  def build_dataset(words, n_words):
      """
      函数功能：将原始的单词表示变成index
      """
      count = [['UNK', -1]]
      count.extend(collections.Counter(words).most_common(n_words - 1))
      # count = [['UNK', -1], ('the', 1061396), ('of', 593677), ('and', 416629) ...]
      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
              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, count, dictionary, reverse_dictionary = build_dataset(vocabulary, vocabulary_size)
  del vocabulary  # 删除已节省内存
  # 输出最常出现的5个单词
  print('Most common words (+UNK)', count[:5])
  # 输出转换后的数据库data，和原来的单词（前10个）
  print('Sample data', data[:10], [reverse_dictionary[i] for i in data[:10]])
  # 我们下面就使用data来制作训练集
  print("+++++++++++++++++")
  data_index = 0
  
  
  # 第三步：定义一个函数，用于生成cbow模型用的batch
  def generate_batch(batch_size, cbow_window):
      global data_index
      assert cbow_window % 2 == 1
      span = 2 * cbow_window + 1
      # 去除中心word: span - 1
      batch = np.ndarray(shape=(batch_size, span - 1), dtype=np.int32)
      labels = np.ndarray(shape=(batch_size, 1), dtype=np.int32)
  
      buffer = collections.deque(maxlen=span)
      for _ in range(span):
          buffer.append(data[data_index])
          # 循环选取 data中数据，到尾部则从头开始
          data_index = (data_index + 1) % len(data)
  
      for i in range(batch_size):
          # target at the center of span
          target = cbow_window
          # 仅仅需要知道context(word)而不需要word
          target_to_avoid = [cbow_window]
  
          col_idx = 0
          for j in range(span):
              # 略过中心元素 word
              if j == span // 2:
                  continue
              batch[i, col_idx] = buffer[j]
              col_idx += 1
          labels[i, 0] = buffer[target]
          # 更新 buffer
          buffer.append(data[data_index])
          data_index = (data_index + 1) % len(data)
  
      return batch, labels
  
  
  num_steps = 100001
  
  if __name__ == '__main__':
      batch_size = 128
      embedding_size = 128  # Dimension of the embedding vector.
      cbow_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.
      # pick 16 samples from 100
      valid_examples = np.array(random.sample(range(valid_window), valid_size // 2))
      valid_examples = np.append(valid_examples, random.sample(range(1000, 1000 + valid_window), valid_size // 2))
      num_sampled = 64  # Number of negative examples to sample.
  
      graph = tf.Graph()
  
      with graph.as_default(), tf.device('/cpu:0'):
  
          # Input data.
          train_dataset = tf.placeholder(tf.int32, shape=[batch_size, 2 * cbow_window])
          train_labels = tf.placeholder(tf.int32, shape=[batch_size, 1])
          valid_dataset = tf.constant(valid_examples, dtype=tf.int32)
  
          # Variables.
          # embedding, vector for each word in the vocabulary
          embeddings = tf.Variable(tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))
          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]))
  
          # Model.
          # Look up embeddings for inputs.
          # this might efficiently find the embeddings for given ids (traind dataset)
          # manually doing this might not be efficient given there are 50000 entries in embeddings
          embeds = None
          for i in range(2 * cbow_window):
              embedding_i = tf.nn.embedding_lookup(embeddings, train_dataset[:, i])
              print('embedding %d shape: %s' % (i, embedding_i.get_shape().as_list()))
              emb_x, emb_y = embedding_i.get_shape().as_list()
              if embeds is None:
                  embeds = tf.reshape(embedding_i, [emb_x, emb_y, 1])
              else:
                  embeds = tf.concat([embeds, tf.reshape(embedding_i, [emb_x, emb_y, 1])], 2)
  
          assert embeds.get_shape().as_list()[2] == 2 * cbow_window
          print("Concat embedding size: %s" % embeds.get_shape().as_list())
          avg_embed = tf.reduce_mean(embeds, 2, keep_dims=False)
          print("Avg embedding size: %s" % avg_embed.get_shape().as_list())
  
          loss = tf.reduce_mean(tf.nn.nce_loss(nce_weights, nce_biases,
                                               labels=train_labels,
                                               inputs=avg_embed,
                                               num_sampled=num_sampled,
                                               num_classes=vocabulary_size))
  
          # Optimizer.
          # Note: The optimizer will optimize the softmax_weights AND the embeddings.
          # This is because the embeddings are defined as a variable quantity and the
          # optimizer's `minimize` method will by default modify all variable quantities
          # that contribute to the tensor it is passed.
          # See docs on `tf.train.Optimizer.minimize()` for more details.
          # Adagrad is required because there are too many things to optimize
          optimizer = tf.train.AdagradOptimizer(1.0).minimize(loss)
  
          # Compute the similarity between minibatch examples and all embeddings.
          # We use the cosine distance:
          norm = tf.sqrt(tf.reduce_sum(tf.square(embeddings), 1, keep_dims=True))
          normalized_embeddings = embeddings / norm
          valid_embeddings = tf.nn.embedding_lookup(normalized_embeddings, valid_dataset)
          similarity = tf.matmul(valid_embeddings, tf.transpose(normalized_embeddings))
  
      with tf.Session(graph=graph) as session:
          tf.global_variables_initializer().run()
          print('Initialized')
          average_loss = 0
          for step in range(num_steps):
              batch_data, batch_labels = generate_batch(batch_size, cbow_window)
              feed_dict = {train_dataset: batch_data, train_labels: batch_labels}
              _, l = session.run([optimizer, loss], feed_dict=feed_dict)
              average_loss += l
              if step % 2000 == 0:
                  if step > 0:
                      average_loss = average_loss / 2000
                      # The average loss is an estimate of the loss over the last 2000 batches.
                  print('Average loss at step %d: %f' % (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 range(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 = 'Nearest to %s:' % valid_word
                      for k in range(top_k):
                          close_word = reverse_dictionary[nearest[k]]
                          log = '%s %s,' % (log, close_word)
                      print(log)
          final_embeddings = normalized_embeddings.eval()
  
  
  # Step 6: 可视化
  # 可视化的图片会保存为“tsne1.png”
  
  def plot_with_labels(low_dim_embs, labels, filename='tsne1.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:
      # pylint: disable=g-import-not-at-top
      from sklearn.manifold import TSNE
      import matplotlib
  
      matplotlib.use('agg')
      import matplotlib.pyplot as plt
  
      # 因为我们的embedding的大小为128维，没有办法直接可视化
      # 所以我们用t-SNE方法进行降维
      tsne = TSNE(perplexity=30, n_components=2, init='pca', n_iter=5000)
      # 只画出500个词的位置
      plot_only = 500
      low_dim_embs = tsne.fit_transform(final_embeddings[:plot_only, :])
      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.')
  