charRNN和word2vec的实现原理

charRNN其实实现的是自动生成的功能,比如说自动生成英文,自动生成古诗,自动生成代码。

下面来说说自动生成古诗的整个项目的思路。

首先我们需要个古诗样本集合,这里面存放了大量的唐诗。

那么我们将面临一个问题,如何去处理这些中文。有一个处理方法是,统计该古诗集中所有用到的中文字,并给每一个中文字打上1,2,3,4,5...这样不重复的数字标签。让后用数字标签替换掉中文形成数字诗集。

那么如何让rnn来达到学习古诗词并且自动生成古诗词的目的呢?

这就要考虑到损失函数是如何来定义的。我们知道损失函数一般是生成值减去实际值的平方然后累加。那么这里的实际值其实是它下一个位置的数字标识。那么在经过多次的迭代之后,rnn网络是可以形成根据当前字符生成下一字符的能力的。

相对于字母来说,汉字的种类比较多,可能会导致模型过大 。 对此再以下两种优化方法:


1. 取最常用的 N 个汉字。将剩下的汉字变成单独一类,并用一个特殊的字符<unk>进行标注 。
2.在输入时,可以加入一层 embedding 层,这个 embedding 层可以将汉字转换为较为稠密的表示 , 它可以代稀疏的独热表示,取得更好的效果 。之所以对字母不使用 embedding ,是因为单个字母不具备任何含义 ,只需要使用独热表示即可 。 而单个汉字还是有高一定实际意义的,因此可以使用 embedding 将真映射到一个较为稠密的空间 。embedding的具体实现逻辑后面介绍。

embeding的使用方式?

在tensorflow中,可以使用下面代码来完成embeding的编码工作。将input和embeding的结构定义输入到embedding_lookup中,得到的结构就是embeding处理后的数据。

embedding = tf.get_variable('embedding', [self.num_classes, self.embedding_size])
self.lstm_inputs = tf.nn.embedding_lookup(embedding, self.inputs)

但是embeding背后使用的word2vec却是要复杂很多的。

首先我们需要搞清楚word2vec解决的是什么样的问题?

word2vec可以形成密集的矩阵来代替独热编码,做到用矩阵唯一代表单词的作用。因为单词的数据集是非常大的,独热编码的维度又是数据集的大小,这个时候使用独热编码来表示单词或汉字明显是不合适的,而word2vec的数据维度一般使用128维就够用了,所以这个时候使用word2vec是非常合适的选择。

词嵌入word2vec的两种方式

CBOW连续词袋模型

核心思想是利用这个词的上下文来预测这个词。

首先考虑用一个词来预测另一词的情况,这个用法其实就是charRNN中的用于处理中文汉字的时的用法。

x1到xv是一个单词或汉字的独热表示,它经过一个全连接层得到隐层h,再经过一个全连接层得到输出成y。其中隐层神经元个数是要小于v的,一般设128,256,512。在这个模型中的实际y值是下个词的独热表示,当这个模型训练完成后,隐层的值就是词的嵌入表示,也就是word2vec。

用多个词来预测一个词的情况

与用一个词来预测一个词的模型相比较,它只是将所有隐层的中做了个相加的操作。

当然也有使用1个词来预测多个词的思想Skip-Gram

Skip-Gram 方法和 CBOW 方法正好相反:使用“出现的词”来预测E “上下文文中词”。如在之前的句子中,是使用“ woman ” ,来预测“ man ”“ fell ”等单词 。 所以,可以把 Skip-Gram 方法看作从一个单词预测另一个单词的问题 。在损失的选择上,和 CBOW 一样,不使用 V 类分类的 Softmax 交叉摘损失, 而是取出一些“噪声词”,训练一个两类分类器(即同样使用 NCE损失)。
 

但是现在依然是存在问题的,现在整个网络是一个v分类器,而我现在并不是要做分类的任务,我们只是想用更少维度的向量来表示单词。解决方法是使用2分类模型来降低计算的复杂度。具体来说,设要预测的目标词汇是“ mat”,会在整个单词表中,随机地取出一些词作为“躁声词汇",如computer”,“ fork ” 。 模型会做一个两类分类:判断一个词汇是否属于“躁声词汇” 。通过优化二分类损失函数来训练模型后,最后得到的模型中的隐含层可以看作是 word2vec 中的“vec”向量 。
 

在编码的过程中我们会看到下面的代码

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

对于输入数据 train inputs ,用一个 tf.nn.embedding_lookup 函数,可以根据 embeddings 变量将真转换成对应的词嵌入向量 embed 。对比 embed和输入数据的标签 train labels ,用 tf.nn.nce loss 函数可以直接走义其 NCE损失。在这里nec其实是有使用实际标签隐性转换为2分类的模型的。

在代码编写过程中还有一个处理是十分关键的。那就是如何去找寻数据和lable之间的对应关系。下面简答介绍一个问题的思路。

例句:我 想 吃 大 餐。

如果我们想通过我 想 大 餐 来预测 吃这个字的话。那么数据集是[我 想 大 餐], 标签集是[ 吃 吃 吃 吃]。这个是使用CBOW的思想。

如果我们想通过 吃 来预测 我 想 大 餐这四个字的话。那么数据集是[ 吃 吃 吃 吃], 标签集是[我 想 大 餐],。这个是使用SKIP-GRAM的思想。

 
发布了23 篇原创文章 · 获赞 1 · 访问量 1371

猜你喜欢

转载自blog.csdn.net/langsiming/article/details/102845527
今日推荐