词向量是自然语言分词在词空间中的表示,词之间的距离代表了分词之间的相似性,我们可以使用gensim,tensorflow等框架非常方便的来实现词向量。但词向量在词空间的分布到底是什么样的,如何更好的理解词向量是一个非常重要的问题。本文将使用tensorbord以及相关的降维技术在三维空间中模拟词向量在高维空间的分布。
词向量的训练是一个无监督的学习过程,这并不是本文讨论的重点。这里只是简单描述一下基本理论。词的表述有两种基本方法:
One hot 用来表示词向量非常简单,但是却有很多问题。1、任意两个词之间都是孤立的,根本无法表示出在语义层面上词语词之间的相关信息,而这一点是致命的。2、我们的词汇表一般都非常大,比如达到百万级别,这样每个词都用百万维的向量来表示简直是内存的灾难。能不能把词向量的维度变小呢?
Dristributed representation可以解决One hot representation的问题,它的思路是通过训练,将每个词都映射到一个较短的词向量上来。所有的这些词向量就构成了向量空间,进而可以用普通的统计学的方法来研究词与词之间的关系。这个较短的词向量维度是多大呢?这个一般需要我们在训练时自己来指定。
词的分布式表示主要可以分为三类:基于矩阵的分布表示、基于聚类的分布表示和基于神经网络的分布表示
词向量的训练有两种方法:
CBOW(Continuous Bag-of-Word Model)又称连续词袋模型,是一个三层神经网络。如下图所示,该模型的特点是输入已知上下文,输出对当前单词的预测。
2.1 skip-gram模型
Skip-gram只是逆转了CBOW的因果关系而已,即已知当前词语,预测上下文。
这里我们使用一组已经训练好的词向量,直接在三维空间中进行可视化操作。主要使用到的语料是北师大训练好的一个300维的语料库,如下图所示:
可以使用此链接下载相关语料:https://github.com/embedding/chinese-word-vectors
这是一个非常庞大的语料,由于语料已经丢失了上下文环境,所以无法进行迁移学习。下面主要分析语料的主要结构,以便于后续读取相关数据。这个语料大小接近3.5G,如下图所示:
语料的基本结构如下:
语料的第一行说明了整个语料包含的单词数量,以及每个单词表示的维度。在这里每一个单词都是由一个300为的向量来表示的。一共有1292607个单词。从第二行开始每一行代表一个单词,用空格隔开,上图中第二行代表单词“,”,后边是他的向量表示。整个语料的整体结构就是这样。这是一个标准的词向量表示文本,可以使用gensim等第三方类库直接读取,但由于本文的目的是为了充分理解词向量所以后面会采用,手动读取词向量的方式来处理语料。
这里采用传统的读取文件的形式读取语料,为了降低计算机处理的数据量,这里我们只读取2000个单词,而不是整个语料。具体代码如下所示:
from tqdm import tqdm # progression bars
import numpy as np
with open('sgns.merge.word','r') as f:
header = f.readline()
vocab_size, vector_size = map(int, header.split())
words, embeddings = [], []
for line in tqdm(range(2000)):
word_list = f.readline().split(' ')
word = word_list[0]
vector = word_list[1:-1]
words.append(word)
embeddings.append(np.array(vector))
print(words[9:10])
print(embeddings[9:10])
输出结果如下图所示:
下面将这些变量保存到到tensorflow的variable中并保存模型,请参照下面代码:
import tensorflow as tf
from tensorflow.contrib.tensorboard.plugins import projector
import os
log_path = 'logs'
with tf.Session() as sess:
# tf.assign():这里是一个将具体数值(即,词向量矩阵)赋值给tf Variable的例子:
X = tf.Variable([0.0], name='embedding')
place = tf.placeholder(tf.float32, shape=[len(words), vector_size])
set_x = tf.assign(X, place, validate_shape=False)
sess.run(tf.global_variables_initializer())
sess.run(set_x, feed_dict={place: embeddings})
# 需要保存一个metadata文件,给词典里每一个词分配一个身份
with open(log_path + '/metadata.tsv', 'w') as f:
for word in tqdm(words):
f.write(word + '\n')
# 写 TensorFlow summary
summary_writer = tf.summary.FileWriter(log_path, sess.graph)
config = projector.ProjectorConfig()
embedding_conf = config.embeddings.add()
embedding_conf.tensor_name = 'embedding:0'
embedding_conf.metadata_path = os.path.join(log_path, 'metadata.tsv')
projector.visualize_embeddings(summary_writer, config)
# 保存模型
# word2vec参数的单词和词向量部分分别保存到了metadata和ckpt文件里面
saver = tf.train.Saver()
saver.save(sess, os.path.join(log_path, "model.ckpt"))
生成文件如下图所示:
使用下面命令开启tensorboard,观察效果