alecxe's answer's intent was to provide suggestions, I would like to provide the actual revised code to depict his suggestions in practice. The below code includes the implementation of all of his suggestions, but using unpacking of variables in the for loop rather than a TextLineReader
. This is because a TextLineReader
returns a tensor, and I do not know of an easy way to go from tensor to TFRecord, especially since the sentences need to be tokenized into a list of words. Also, this code assumes that the .tsv file is of proper format, and expects a very specific format (float, string, string). If errors in .tsv format are possible, you will have to modify the code for error handling.
Another thing to note, when it comes to the 'micro-optimizations' for doing name look-ups once, it may be better or equivalent performance to import the specific name look-up in the function and set its namespace as 'name'. For example with Example: import tf.train.Example as Example
. The reason to keep it in the function, is to avoid muddying the namespace in the whole file. If you do not care about that, then just put it with the rest of the imports at the top. I am uncertain, if this is a better approach so I went with what was more readable and understandable.
The use of sentence_dict
is to implement the trade off between memory for speed. If you're feeling especially ambitious, which I was not, you could create a personal function for sentence/string tokenization based on nltk's that will also store a word_dict
so that new sentences with previously seen words will have constant time look up, but this may cause more time to compute due to each word needing to be checked before tokenization. Nonetheless, it is an idea that may be worth exploring.
I was doubtful on the speed boost from the sentence_dict
due to not knowing if checking every sentence would take less run-time than just tokenizing all the words, but after comparing the code with and without the sentence_dict
, I found a noteable decrease in run-time (by about a tenth of a second). This was using the SEMEVAL STS 2017 training data for the task of English to English semantic matching.
import os
import csv
import tensorflow as tf
import nltk
def tsv_to_tfrec(filename):
"""
Creates tfrecord equivalent to .tsv provided by filename. Assumes proper
.tsv with equal columns in each row.
@param filename: filename of .tsv to be converted to TFRecord
"""
with open(filename, 'rb') as tsv, tf.python_io.TFRecordWriter(
os.path.splitext(filename)[0] + ".tfrecords") as tfrec:
read_tsv = csv.reader(tsv, delimiter='\t', quoting=csv.QUOTE_NONE)
# Trading off memory for speed
sentence_dict = {}
# Micro-Optimize by name look-up once
Example = tf.train.Example
Feature = tf.train.Feature
Features = tf.train.Features
FloatList = tf.train.FloatList
BytesList = tf.train.BytesList
for rate, sent1, sent2 in read_tsv:
if sent1 not in sentence_dict:
sentence_dict[sent1] = nltk.word_tokenize(sent1)
if sent2 not in sentence_dict:
sentence_dict[sent2] = nltk.word_tokenize(sent2)
example = Example(features=Features(feature={
"rate": Feature(float_list=FloatList(
value=[float(rate)])),
"sent1": Feature(bytes_list=BytesList(
value=sentence_dict[sent1])),
"sent2": Feature(bytes_list=BytesList(
value=sentence_dict[sent2]))
}))
tfrec.write(example.SerializeToString())