在tensorflow/nmt项目中,训练数据和推断数据的输入使用了新的Dataset API,应该是tensorflow 1.2之后引入的API,方便数据的操作。如果你还在使用老的Queue和Coordinator的方式,建议升级高版本的tensorflow并且使用Dataset API。
本教程将解析tensorflow nmt项目中数据的具体处理过程,你将看到文本数据如何转化为模型所需要的数字,以及中间的张量的维度是怎么样的,batch_size和其他超参数又是如何作用的。
训练数据的处理
先来看看训练数据的处理。训练数据的处理比推断数据的处理稍微复杂一些,弄懂了训练数据的处理过程,就可以很轻松地理解推断数据的处理。
训练数据的处理代码位于nmt/utils/iterator_utils.py文件内的函数。
函数的参数
我们先来看看这个函数所需要的参数是什么意思:
我们首先搞清楚几个重要的参数是怎么来的。
上面的解释,如果有不清楚的,可以查看我之前一片介绍超参数的文章:
tensorflow_nmt的超参数详解(https://github.com/luozhouyang/csdn-blogs/blob/master/tensorflow_nmt/tensorflow_nmt_hparams.md)
和是我们的训练数据集,他们是逐行一一对应的。比如我们有两个文件和分别对应训练数据的源数据和目标数据,那么它们的Dataset如何创建的呢?其实利用Dataset API很简单:
这就是上述函数中的两个参数和的由来。
和是什么呢?同样顾名思义,就是这两个分别代表源数据词典的查找表和目标数据词典的查找表,实际上查找表就是一个字符串到数字的映射关系。当然,如果我们的源数据和目标数据使用的是同一个词典,那么这两个查找表的内容是一模一样的。很容易想到,肯定也有一种数字到字符串的映射表,这是肯定的,因为神经网络的数据是数字,而我们需要的目标数据是字符串,因此它们之间肯定有一个转换的过程,这个时候,就需要我们的reverse_vocab_table来作用了。
我们看看这两个表是怎么构建出来的呢?代码很简单,利用tensorflow库中定义的lookup_ops即可:
我们可以发现,创建这两个表的过程,就是将词典中的每一个词,对应一个数字,然后返回这些数字的集合,这就是所谓的词典查找表。效果上来说,就是对词典中的每一个词,从0开始递增的分配一个数字给这个词。
那么到这里你有可能会有疑问,我们词典中的词和我们自定义的标记等是不是有可能被映射为同一个整数而造成冲突?这个问题该如何解决?聪明如你,这个问题是存在的。那么我们的项目是如何解决的呢?很简单,那就是将我们自定义的标记当成词典的单词,然后加入到词典文件中,这样一来,操作就把标记当成单词处理了,也就就解决了冲突!
具体的过程,本文后面会有一个例子,可以为您呈现具体过程。
如果我们指定了参数,那么返回的源单词查找表和目标单词查找表是一样的。我们还可以指定一个default_value,在这里是,实际上就是。如果不指定,那么默认值为。这就是查找表的创建过程。如果你想具体的知道其代码实现,可以跳转到tensorflow的C++核心部分查看代码(使用PyCharm或者类似的IDE)。
数据集的处理过程
该函数处理训练数据的主要代码如下:
我们逐步来分析,这个过程到底做了什么,数据张量又是如何变化的。
我们知道,对于源数据和目标数据,每一行数据,我们都可以使用一些标记来表示数据的开始和结束,在本项目中,我们可以通过和两个参数指定句子开始标记和结束标记,默认值分别为和。本部分代码一开始就是将这两个句子标记表示成一个整数,代码如下:
过程很简单,就是通过两个字符串到整形的查找表,根据和的字符串,找到对应的整数,用改整数来表示这两个标记,并且将这两个整数转型为int32类型。
接下来做的是一些常规操作,解释如注释:
接下来就是重点了,我将用注释的形式给大家解释:
其实到这里,基本上数据已经处理好了,可以拿去训练了。但是有一个问题,那就是我们的每一行数据长度大小不一。这样拿去训练其实是需要很大的运算量的,那么有没有办法优化一下呢?有的,那就是数据对齐处理。
如何对齐数据
数据对齐的代码如下,使用注释的方式来解释代码:
这样就完成了数据的对齐,并且将数据集按照完成了分批。
num_buckets分桶到底起什么作用
起作用的代码如下:
顾名思义就是桶的数量,那么这个桶用来干嘛呢?我们先看看上面两个函数到底做了什么。
首先是判断我们指定的参数是否大于1,如果是那么就进入到上述的作用过程。
是做什么的呢?通过源码和注释我们发现,它是用来将我们的数据集(由源数据和目标数据成对组成)按照一定的方式进行分类的。具体说来就是,根据我们数据集每一行的数据长度,将它放到合适的桶里面去,然后返回该数据所在桶的索引。
这个分桶的过程很简单。假设我们有一批数据,他们的长度分别为,我们规定一个bucket_width为10,那么我们的数据分配到具体的桶的情况是怎么样的呢?因为桶的宽度为10,所以第一个桶放的是小于长度10的数据,第二个桶放的是10-20之间的数据,以此类推。
所以,要进行分桶,我们需要知道数据和bucket_width两个条件。然后根据一定的简单计算,即可确定如何分桶。上述代码首先根据来计算,然后分桶,然后返回数据分到的桶的索引。就是这么简单的一个过程。
那么,你或许有疑问了,我干嘛要分桶呢?你仔细回想下刚刚的过程,是不是发现长度差不多的数据都分到相同的桶里面去了!没错,这就是我们分桶的目的,相似长度的数据放在一起,能够提升计算效率!!!
然后要看第二个函数,这个函数做了什么呢?其实就做了一件事情,就是把刚刚分桶好的数据,做一个对齐!!!
那么通过分桶和对齐操作之后,我们的数据集就已经成为了一个对齐(也就是说有固定长度)的数据集了!
回到一开始,如果我们的参数不满足条件呢?那就直接做对齐操作!看代码便知!
至此,分桶的过程和作用你已经清楚了。
至此,数据处理已经结束了。接下来就可以从处理好的数据集获取一批一批的数据来训练了。
那么如何一批一批获取数据呢?答案是使用迭代器。获取Dataset的迭代器很简单,tensorflow提供了API,代码如下:
通过迭代器的方法,就可以获取之前我们处理好的批量数据啦!
tensorflow nmt的数据预处理过程已经结束了。欢迎和我交流。
个人公众号,不定时发布文章,你或许会感兴趣:
领取专属 10元无门槛券
私享最新 技术干货