本笔记记录一下鄙人在使用tf的心得,好让自己日后可以回忆一下。其代码内容都源于tf的tutorial里面的Vector Representations of Words。

现在我们一起来实现通过tf实现word2vec吧。

代码地址:https://github.com/tensorflow/tensorflow/blob/r1.2/tensorflow/examples/tutorials/word2vec/word2vec_basic.py


step1 数据集

url = 'http://mattmahoney.net/dc/'def maybe_download(filename, expected_bytes):passfilename = maybe_download('text8.zip', 31344016)

filename 是我们的待处理的目标文件。其实是它是在http://mattmahoney.net/dc/text8.zip 里面,而这个函数就判断本地时候存在该文件,若没有就网上读取(鄙人就先下载下来)。我不知道为毛它要判断文件大小跟预期一样,也不影响我们后面的工作。


step2 读取数据

def read_data(filename):"""提取第一个文件当中的词列表:param filename::return:"""with zipfile.ZipFile(filename) as f:data = tf.compat.as_str(f.read(f.namelist()[0])).split()return data

这里不用细说啦。因为都是简单的i/o

vocabulary = read_data(filename)
vocabulary_size = 50000

读取了文件里面的词语后,为了方便,我们先定义自己的词典大小为5W。


step3 建立词典

def build_dataset(words, n_words):"""建立字典数据库:param words::param n_words::return:"""count = [['UNK', -1]]# 记录前49999个高频词,各自出现的次数count.extend(collections.Counter(words).most_common(n_words - 1))# Key value pair : {word: dictionary_index}dictionary = dict()for word, _ in count:dictionary[word] = len(dictionary)# 记录每个词语对应与词典的索引data = list()unk_count = 0for word in words:if word in dictionary:index = dictionary[word]else:index = 0unk_count += 1data.append(index)# 记录没有在词典中的词语数量count[0][1] = unk_countreversed_dictionary = dict(zip(dictionary.values(), dictionary.keys()))return data, count, dictionary, reversed_dictionary
  • count:记录词语和对应词频的key-value
  • data:记录文本内的每一个次对应词典(dictionary)的索引
  • dictionary:记录词语和相应词典索引
  • reversed_dicitonary:记录词典索引和相应的词语,跟dictionary的key-value相反

这里的['UNK', -1]记录这一些词典没有记录的词语,因为我们只拿文本中出现次数最多的前49999词作为词典中的词语。这意味着有一些我们不认识的词语啊。那我们就将其当作是我们词典的“盲区”,不认识的词(unknown words)简称UNK。

# 词语索引,每个词语词频,词典,词典的反转形式
data, count, dictionary, reverse_dictionary = build_dataset(vocabulary, vocabulary_size)

调用该函数我们就获得词典的内容


step4 开始建立skip-gram模型需要的数据集合(data, label)

对于传统的ML或者DL都会使用有监督型的数据进行训练。对于skip-gram模型,我需要的数据集合应该是{(x)data: target word, (y)label: context words}。它跟CBOW是截然不同的,因为CBOW是需要通过上下文推断目标词语,所以需要的数据集合是{data: context words, label target word}。现在我们根据文本内容和从文本获得词典,我们开始建立训练数据集合。

# 给skip-gram模型生成训练集合
def generate_batch(batch_size, num_skips, skip_window):""":param batch_size: 训练批次(batch)的大小:param num_skips:  采样的次数:param skip_window:  上下文的大小:return:"""global data_indexassert batch_size % num_skips == 0assert num_skips <= 2 * skip_windowbatch = np.ndarray(shape=(batch_size), dtype=np.int32)labels = np.ndarray(shape=(batch_size, 1), dtype=np.int32)# 上下文的组成:[skip_window target skip_window]span = 2 * skip_window + 1# 缓冲区buffer = collections.deque(maxlen=span)for _ in range(span):buffer.append(data[data_index])data_index = (data_index + 1) % len(data)for i in range(batch_size // num_skips):target = skip_windowtarget_to_avoid = [skip_window]for j in range(num_skips):#while target in target_to_avoid:target = random.randint(0, span - 1)target_to_avoid.append(target)# 记录输入数据(中心词)batch[i * num_skips + j] = buffer[skip_window]# 记录输入数据对应的类型(上下文内容)labels[i * num_skips + j, 0] = buffer[target]buffer.append(data[data_index])data_index = (data_index + 1) % len(data)data_index = (data_index + len(data) - span) % len(data)return batch, labels

这里我们得到的batch就是我们想要的输入词语/数据(词语在词典当中的索引),另外label是batch对应的目标词语/数据(词语在词典当中的索引)。这里举个例子,我们现在给定batch_size为8,num_skips为2,skip_window=1,给出文本为

"I am good at studying and learning ML. However, I don't like to read the English document."

我粗略算算词典

['I', 'am', 'good', 'at', 'studying', 'and', 'learning', 'ML', 'However', 'I', 'don't', 'like', 'to', 'read', the', 'English', 'document']

根据generate_batch的内容和给定参数,我们第一次获得内容应该是

['I', 'am', 'good', 'at', 'studying', 'and', 'learning', 'ML']

我们的上下文窗口(span)应该是 2 * 1 + 1 = 3。也就是窗口应该是

buffer=['I', 'am', 'good']

显然target应该是'am'也就是为buffer[skip_window]context word应该是['I', 'good']。这就构成了{x: data, y: label}之间的关系。
对于skip-gram模型的数据集合

  • {(x)data: 'am', (y)label: 'I'}
  • {(x)data: 'am', (y)label: 'good'}

如此类推。那num_skips有啥用呢?其实num_skips意味着需要对buffer进行多少次才采样,才开始对下一个buffer进行采样。


step5 开始建立skip-gram模型(重点来了)

batch_size = 128
embedding_size = 128 # 嵌入向量的维度
skip_window = 1     # 上下文的词数
num_skips = 2       # 多少次后重用输入的生成类别# 我们使用随机邻居样本生成评估集合,这里我们限定了
# 评估样本一些数字ID词语,这些ID是通过词频产生
valid_size = 16
valid_window = 100
valid_examples = np.random.choice(valid_window, valid_size, replace=False)
num_sample = 64graph = 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))# 根据batch的大小设置输入数据的batchembed = 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_sample,num_classes=vocabulary_size))# learning rate 1.0optimizer = 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)# 计算cosine相似度similarity = tf.matmul(valid_embeddings, normalized_embeddings, transpose_b=True)init = tf.global_variables_initializer()

这里可能没有之前这么简单了,因为不懂word2vec数学原理的人,完全看不懂代码,尽管你精通Python,也不知道为毛有这行代码和代码的含义。这里我不多讲word2vec的数学原理,迟点我会再一遍文章讲解word2vec的原理和疑问。这里我给出一篇我看过的详细的文章word2vec的数学原理,大家可以先阅览一下。我在这里稍微讲一下代码和附带的原理内容。

        # 随机生成初始词向量embeddings = tf.Variable(tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0))

大家都知道word2vec就是说把词语变成向量形式,而在CBOW和skip-gram模型中,词向量是副产品,真正的目的是推断出上下文内容。这里就来一个要点了(是鄙人私以为的):

在模型的训练过程中,调整词向量和不断是推断逼近目标词语是同时进行。也就是说调整词向量->优化推断->调整词向量->优化推断->调整词向量->优化推断.... 最后达到两者同时收敛。这就是我们最后的目标。这是我从EM算法中类比获得的想法,关于EM算法,我会在之后添加文章(算法推导+代码)。

在DL和ML中我们都说到损失函数,不断优化损失函数使其最小,是我们的目标。这里的损失函数是什么呢?那就是

        tf.nn.nce_loss(weights=nce_weights,biases=nce_biases,labels=train_labels,inputs=embed,num_sampled=num_sample,num_classes=vocabulary_size)

我们刚刚说到要把推断出哪个词应该出现在上下文当中,就是涉及到一个概率问题了。既然是推断那就是要比较大小啦。那就是把词典中所有的词的有可能出现在上下文的概率都算一遍吗?确实!在早期word2vec论文发布时,就是这么粗暴。现在就当然不是啦。那就是用negative sample来推断进行提速啦。

我们知道在训练过程中,我们都知道label是哪个词。这意味着其他词对于这个样本就是negative了。那就好办啦。我就使得label词的概率最大化,其他词出现的概率最小化。当中涉及的数学知识就是Maximum likelihood 最大似然估计。不懂的回去复习呗。

之后我们用梯度下降法进行训练,这样我们就得到训练模型了。


step6 开始进行无耻的训练

num_steps = 100001with tf.Session(graph=graph) as session:# 初始化变量init.run()print("Initialized")average_loss = 0for 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}_, loss_val = session.run([optimizer, loss], feed_dict=feed_dict)average_loss += loss_valif step % 2000 == 0:if step > 0:average_loss /= 2000print('Average loss at step ', step, ': ', average_loss)average_loss = 0if step % 10000 == 0:sim = similarity.eval()for i in xrange(valid_size):valid_word = reverse_dictionary[valid_examples[i]]top_k = 8nearest = (-sim[i, :]).argsort()[1: top_k + 1]log_str = 'Nearest to %s: ' % valid_wordfor 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()

在每次训练中我们都给数据模型喂养(feed)一小批数据(batch_input, batch_labels)。这些数据是通过generate_batch()生成的。通过暴力的迭代,我们最后得到最终词向量(final_embedding)。在训练过程中,每2000次迭代打印损失值,每10000次迭代打印校验词的相似词(通过cosin相似度来判断)。

最后还差一个词向量降维后的图片,我迟点不上。现在准备煮饭咯....


作者:Salon_sai链接:http://www.jianshu.com/p/1624ede1f2ac來源:简书著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Tensorflow学习笔记——word2vec相关推荐

  1. Tensorflow学习笔记6:解决tensorflow训练过程中GPU未调用问题

    Tensorflow学习笔记6:解决tensorflow训练过程中GPU未调用问题 参考文章: (1)Tensorflow学习笔记6:解决tensorflow训练过程中GPU未调用问题 (2)http ...

  2. tensorflow学习笔记(三十二):conv2d_transpose (解卷积)

    tensorflow学习笔记(三十二):conv2d_transpose ("解卷积") deconv解卷积,实际是叫做conv_transpose, conv_transpose ...

  3. tensorflow学习笔记二——建立一个简单的神经网络拟合二次函数

    tensorflow学习笔记二--建立一个简单的神经网络 2016-09-23 16:04 2973人阅读 评论(2) 收藏 举报  分类: tensorflow(4)  目录(?)[+] 本笔记目的 ...

  4. tensorflow学习笔记——使用TensorFlow操作MNIST数据(1)

    续集请点击我:tensorflow学习笔记--使用TensorFlow操作MNIST数据(2) 本节开始学习使用tensorflow教程,当然从最简单的MNIST开始.这怎么说呢,就好比编程入门有He ...

  5. TensorFlow学习笔记(二):快速理解Tutorial第一个例子-MNIST机器学习入门 标签: 机器学习SoftmaxTensorFlow教程 2016-08-02 22:12 3729人阅

    TensorFlow学习笔记(二):快速理解Tutorial第一个例子-MNIST机器学习入门 标签: 机器学习SoftmaxTensorFlow教程 2016-08-02 22:12 3729人阅读 ...

  6. Tensorflow学习笔记2:About Session, Graph, Operation and Tensor

    简介 上一篇笔记:Tensorflow学习笔记1:Get Started 我们谈到Tensorflow是基于图(Graph)的计算系统.而图的节点则是由操作(Operation)来构成的,而图的各个节 ...

  7. Win10:tensorflow学习笔记(4)

    前言 学以致用,以学促用.输出检验,完整闭环. 经过前段时间的努力,已经在电脑上搭好了深度学习系统,接下来就要开始跑程序了,将AI落地了. 安装win10下tensforlow 可以参照之前的例子:w ...

  8. Win10: tensorflow 学习笔记(3)

    前言 学以致用,以学促用.输出检验,完整闭环. 怕什么真理无穷,进一寸有一寸的欢喜--胡适 经过前段时间的努力,已经在电脑上搭好了深度学习系统,接下来就要开始跑程序了,将AI落地了. 安装win10下 ...

  9. win10:tensorflow学习笔记(2)

    目录: 前言 Tensorflow的故事 1Tensorflow和其他框架的对比 2Tesorflow 目前进展 3大杀器tensorboard 尾声 前言 经过前段时间的努力,已经在电脑上搭好了深度 ...

最新文章

  1. CentOS的Gearman安装与使用无错版
  2. 2017/10/12 表格与表单
  3. 为什么我又喜欢过年了
  4. 关于fckEditor的功能配置-PHP版
  5. bat贪吃蛇游戏代码_100行python代码,轻松完成贪吃蛇小游戏
  6. 百度广告点击软件_结束了,百度 “毒瘤” 广告!
  7. Mariadb使用总结
  8. Java中String对象存储
  9. Mybatis JPA-集成方案+代码解析
  10. Flutter TextField 限制只允许输入数字,字母,小数,设置限制小数位数
  11. SDUT OJ 3403 数据结构实验之排序六:希尔排序
  12. OFFICE技术讲座:段落五种对齐方式的说明
  13. R2V软件自动矢量化----体验篇
  14. 阿里云OSS图片缩放处理
  15. 证明最小码距与纠检错图像_最小码距和检错纠错能力关系
  16. python价格预测模型_Python 机器学习教程: 预测Airbnb 价格(2)
  17. 脑电图(EEG)信号去噪方法简述
  18. elasticsearch常用的curl命令
  19. 分页查询时报错:The bean ‘localeResolver‘, defined in class path resource [com/botany/spore/core/config/Comm
  20. 计算机电缆yjv,zr电缆(yjv电缆与zryjv电缆区别)

热门文章

  1. Kafka之Purgatory Redesign Proposal (翻译)
  2. 154在屏幕中绘图时设置透明度(扩展知识:为图片视图添加点击手势识别器,来实现点击事件操作)...
  3. 横向经济联合的理论认识
  4. (转)Sql中的constraint
  5. STM32学习笔记——DMA控制器(向原子哥学习)
  6. Oracle函数大全2
  7. c 运算符重载总结
  8. 50个python库
  9. 科大星云诗社动态20201205
  10. 科大星云诗社动态20220112