如何用TensorFlow训练词向量

原创 2017年08月24日 15:21:57

前言

前面在《谈谈谷歌word2vec的原理》文章中已经把word2vec的来龙去脉说得很清楚了,接下去这篇文章将尝试根据word2vec的原理并使用TensorFlow来训练词向量,这里选择使用skip-gram模型。

语料库的准备

这里仅仅收集了网上关于房产新闻的文章,并且将全部文章拼凑到一起形成一个语料库。

skip-gram简要说明

skip-gram核心思想可以通过下图来看,假设我们的窗口大小为2,则对于文本”The quick brown fox jumps over the lazy dog.”,随着窗口的滑动将产生训练样本。比如刚开始是(the,quick)(the,brown)两个样本,右移一步后训练样本为(quick,the)(quick,brown)(quick,fox),继续右移后训练样本为(brown,the)(brown,quick)(brown,fox)(brown,jumps),接着不断右移产生训练样本。skip-gram模型的核心思想即是上面所说。

这里写图片描述

预料加载&分词

def read_data(filename):
    with codecs.open(filename, 'r', encoding='utf-8') as f:
        data = f.read()
        seg_list = jieba.cut(data, cut_all=False)
        text = tf.compat.as_str("/".join(seg_list)).split('/')
    return text

filename = "D:\\data6\\house_train\\result.txt"

vocabulary = read_data(filename)

实现对语料库文件的加载并且对其进行分词。filename指定语料库文件,而分词使用jieba来实现,最后返回一个包含语料库所有词的list。

构建词典

vocabulary_size = 50000

def build_dataset(words, n_words):
    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
            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

这里我们是要建立一个大小为50000的词汇,vocabulary是从语料集中获取的所有单词,统计vocabulary每个单词出现的次数,而且是取出现频率最多的前49999个词,count词典方便后面查询某个单词对应出现的次数。接着我们建立dictionary词典,它是单词与索引的词典,方便后面查询某个单词对应的索引位置。接着我们将vocabulary所有单词转换成索引的形式保存到data中,凡是不在频率最高的49999个词当中的我们都当成是unknown词汇并且将其索引置为0,此过程顺便统计vocabulary包含了多少个unknown的词汇。另外还要建立一个反向索引词典reversed_dictionary,可以通过位置索引得到单词。

获取批数据

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
    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
        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
    data_index = (data_index + len(data) - span) % len(data)
    return batch, labels

提供一个生成训练批数据的函数,batch_size是我们一次取得一批样本的数量,num_skip则可以看成是我们要去某个词窗口内的词的数量,比如前面我们说到的窗口大小为2,则某个词附近一共有4个词最终最多可以组成4个训练样本,但如果你只需要组成2个样本的话则通过num_skip来设置。skip_window则用于设定窗口的大小。取样本时是在整个vocabulary通过滑动窗口进行的,得到的batch和labels都是单词对应的词典索引,这对后面运算提供了方便。

构建图

graph = tf.Graph()
with graph.as_default():
    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)

    with tf.device('/cpu:0'):
        embeddings = tf.Variable(tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))
        embed = tf.nn.embedding_lookup(embeddings, train_inputs)

        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]))

    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))

    optimizer = tf.train.GradientDescentOptimizer(1.0).minimize(loss)

    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, normalized_embeddings, transpose_b=True)

    init = tf.global_variables_initializer()

train_inputs是一个[batch_size]形状的输入占位符,它表示一批输入数据的索引。train_labels是一个[batch_size, 1]形状的正确的分类标签,它表示一批输入对应的正确的分类标签。

embeddings变量用来表示词典中所有单词的128维词向量,这些向量是会在训练过程中不断被更新的,它是一个[vocabulary_size, embedding_size]形状的矩阵,这里其实是[50000,128],因为我们设定词汇一共有50000个单词,且它的元素的值都在-1到1之间。

然后通过embedding_lookup函数根据索引train_inputs获取到一批128维的输入embed。

接着使用NCE作为损失函数,根据词汇量数量vocabulary_size以及词向量维度embedding_size构建损失函数即可,NCE是负采样损失函数,也可以试试用其他的损失函数。nce_weights和nce_biases是NCE过程的权重和偏置,取平均后用梯度下降法优化损失函数。

最后对embeddings进行标准化,得到标准的词向量,再计算所有词向量与我们选来校验的词的相似性(距离)。

创建会话

with tf.Session(graph=graph) as session:
    init.run()
    average_loss = 0
    for step in range(num_steps):
        batch_inputs, batch_labels = generate_batch(batch_size, num_skips, skip_window)
        feed_dict = {train_inputs: batch_inputs, train_labels: batch_labels}
        _, loss_val = session.run([optimizer, loss], feed_dict=feed_dict)
        average_loss += loss_val

        if step % 2000 == 0:
            if step > 0:
                average_loss /= 2000
            print('Average loss at step ', step, ': ', average_loss)
            average_loss = 0

        if step % 10000 == 0:
            sim = similarity.eval()
            for i in range(valid_size):
                valid_word = reverse_dictionary[valid_examples[i]]
                top_k = 8
                nearest = (-sim[i, :]).argsort()[1:top_k + 1]
                log_str = 'Nearest to %s:' % valid_word
                for k in range(top_k):
                    close_word = reverse_dictionary[nearest[k]]
                    log_str = '%s %s,' % (log_str, close_word)
                print(log_str)
    final_embeddings = normalized_embeddings.eval()

创建会话开始训练,设置需要训练多少轮,由num_steps指定。然后通过generate_batch获取到一批输入及对应标签,指定优化器对象和损失函数对象开始训练,每训练2000轮输出看下具体损失,每10000轮则使用校验数据看看他们最近距离的8个是什么词。

降维画图

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)

plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
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, :])
labels = [reverse_dictionary[i] for i in range(plot_only)]
plot_with_labels(low_dim_embs, labels)

选取300个词汇并使用TSNE对其进行降维然后画图。

这里写图片描述

github

https://github.com/sea-boat/DeepLearning-Lab

========广告时间========

公众号的菜单已分为“分布式”、“机器学习”、“深度学习”、“NLP”、“Java深度”、“Java并发核心”、“JDK源码”、“Tomcat内核”等,可能有一款适合你的胃口。

鄙人的新书《Tomcat内核设计剖析》已经在京东销售了,有需要的朋友可以购买。感谢各位朋友。

为什么写《Tomcat内核设计剖析》

=========================

欢迎关注:

这里写图片描述

版权声明:本文为博主原创文章,未经博主允许不得转载。

谈谈谷歌word2vec的原理

word2vec在NLP领域中,为了能表示人类的语言符号,一般会把这些符号转成一种数学向量形式以方便处理,我们把语言单词嵌入到向量空间中就叫词嵌入(word embedding)。谷歌开源的word2...

『RNN 监督序列标注』笔记-第三章 神经网络

『RNN 监督序列标注』笔记-第三章 神经网络多层感知机(Multilayer Perceptrons)多层感知机的输出仅仅取决于当前的输入,因此 MLPs 更适用于模式分类而非序列标注任务。仅仅具有...

tensorflow : 使用预训练词向量

目前使用深度网络进行文本任务模型训练时,第一步应该是将文本转为词向量进行处理。但一般词向量的效果跟语料的大小有关,而处理任务的语料不足支持我们的实验,这时就需要使用网上公开的大规模语料训练词向量。1、...
  • lxg0807
  • lxg0807
  • 2017年05月19日 10:21
  • 4105

Tensorflow实战学习(十八)【词向量、维基百科语料库训练词向量模型】

词向量嵌入需要高效率处理大规模文本语料库。word2vec。简单方式,词送入独热编码(one-hot encoding)学习系统,长度为词汇表长度的向量,词语对应位置元素为1,其余元素为0。向量维数很...
  • WuLex
  • WuLex
  • 2017年11月20日 09:23
  • 123

从零开始使用tensorflow(2)——词向量

前面记录了安装过程,现在开始使用词向量。 一.对tf的肤浅认识 首先是tf的基本总结(时间有限,认识比较肤浅): (1). 使用图来表示计算; (2). 在session中执行图; (3...

TensorFlow学习笔记3:词向量

上篇博文讲了如何构建一个简单的CNN模型,并运行在MNIST数据集上。下面讲述一下如何在TensorFlow中生成词向量(Word Embedding),使用的模型来自Mikolov et a...
  • lyb3b3b
  • lyb3b3b
  • 2017年06月19日 20:19
  • 253

Windows下使用Word2vec继续词向量训练

word2vec是Google在2013年提出的一款开源工具,其是一个Deep Learning(深度学习)模型(实际上该模型层次较浅,严格上还不能算是深层模型,如果word2vec上层再套一层与具体...

使用预训练的word2vec词向量

以谷歌开源google news(bin)为例。快速读取word2vec预训练词向量的方法
  • lics999
  • lics999
  • 2017年12月02日 16:22
  • 84

py2.7 : 《机器学习实战》 朴素贝叶斯 1.12号 4.5.3 训练算法:从词向量计算概率

PS:朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法 4.5.1:准备数据:从文本中构建词向量# -*- coding:utf-8 -*- def loadDataSet(): po...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:如何用TensorFlow训练词向量
举报原因:
原因补充:

(最多只允许输入30个字)