我有一个GCMLE实验,我正在尝试升级input_fn
以使用新的tf.data
功能。我已根据此sample
def input_fn(...):
dataset = tf.data.Dataset.list_files(filenames).shuffle(num_shards) # shuffle up the list of input files
dataset = dataset.interleave(lambda filename: # mix together records from cycle_length number of shards
tf.data.TextLineDataset(filename).skip(1).map(lambda row: parse_csv(row, hparams)), cycle_length=5)
if shuffle:
dataset = dataset.shuffle(buffer_size = 10000)
dataset = dataset.repeat(num_epochs)
dataset = dataset.batch(batch_size)
iterator = dataset.make_one_shot_iterator()
features = iterator.get_next()
labels = features.pop(LABEL_COLUMN)
return features, labels
我的parse_csv
与之前使用的相同,但目前无效。我可以解决一些问题,但我不完全理解为什么我遇到了这些问题。这是我的parse_csv()函数的开始
def parse_csv(..):
columns = tf.decode_csv(rows, record_defaults=CSV_COLUMN_DEFAULTS)
raw_features = dict(zip(FIELDNAMES, columns))
words = tf.string_split(raw_features['sentences']) # splitting words
vocab_table = tf.contrib.lookup.index_table_from_file(vocabulary_file = hparams.vocab_file,
default_value = 0)
....
此tf.string_split()
停止工作且错误为ValueError: Shape must be rank 1 but is rank 0 for 'csv_preprocessing/input_sequence_generation/StringSplit' (op: 'StringSplit') with input shapes: [], [].
- 通过raw_features['sentences']
将[raw_features['sentences']]
打包到张量中可以很容易地解决这个问题,但我做了不明白为什么这个dataset
方法需要这个?旧版本如何才能正常工作?对于与我的模型的其余部分匹配的形状,我最终需要通过words = tf.squeeze(words, 0)
在末尾删除这个额外的维度,因为我添加了这个"不必要的"对张量的维度。
无论出于何种原因,我也收到错误,表格未初始化tensorflow.python.framework.errors_impl.FailedPreconditionError: Table not initialized.
但是,此代码与我的旧input_fn()
完全正常(见下文),所以我不会# 39;我知道为什么我现在需要初始化表格?我还没有想出这个部分的解决方案。我缺少什么能够在我的parse_csv函数中使用tf.contrib.lookup.index_table_from_file
?
作为参考,这是我的旧input_fn()仍然可以工作:
def input_fn(...):
filename_queue = tf.train.string_input_producer(tf.train.match_filenames_once(filenames),
num_epochs=num_epochs, shuffle=shuffle, capacity=32)
reader = tf.TextLineReader(skip_header_lines=skip_header_lines)
_, rows = reader.read_up_to(filename_queue, num_records=batch_size)
features = parse_csv(rows, hparams)
if shuffle:
features = tf.train.shuffle_batch(
features,
batch_size,
min_after_dequeue=2 * batch_size + 1,
capacity=batch_size * 10,
num_threads=multiprocessing.cpu_count(),
enqueue_many=True,
allow_smaller_final_batch=True
)
else:
features = tf.train.batch(
features,
batch_size,
capacity=batch_size * 10,
num_threads=multiprocessing.cpu_count(),
enqueue_many=True,
allow_smaller_final_batch=True
)
labels = features.pop(LABEL_COLUMN)
return features, labels
UPDATE TF 1.7
我正在重新审视TF 1.7(它应该具有@mrry回答中提到的所有TF 1.6功能),但我仍然无法复制该行为。对于我的旧input_fn()
,我能够以13步/秒为单位。我正在使用的新功能如下:
def input_fn(...):
files = tf.data.Dataset.list_files(filenames).shuffle(num_shards)
dataset = files.apply(tf.contrib.data.parallel_interleave(lambda filename: tf.data.TextLineDataset(filename).skip(1), cycle_length=num_shards))
dataset = dataset.apply(tf.contrib.data.map_and_batch(lambda row:
parse_csv_dataset(row, hparams = hparams),
batch_size = batch_size,
num_parallel_batches = multiprocessing.cpu_count()))
dataset = dataset.prefetch(1)
if shuffle:
dataset = dataset.shuffle(buffer_size = 10000)
dataset = dataset.repeat(num_epochs)
iterator = dataset.make_initializable_iterator()
features = iterator.get_next()
tf.add_to_collection(tf.GraphKeys.TABLE_INITIALIZERS, iterator.initializer)
labels = {key: features.pop(key) for key in LABEL_COLUMNS}
return features, labels
我相信我正在关注所有performance guildines,例如1)使用prefetch 2)使用map_and_batch和num_parallel_batches = cores 3)使用parallel_interleave 4)在重复之前应用shuffle。我没有使用的唯一步骤是缓存建议,但是期望它真的只对第一个以外的时期有帮助,并且首先应用交错,预取和随机播放。" - 但是我发现在map_and_batch之后有预取和随机播放速度提高了~10%。
BUFFER ISSUE
我注意到的第一个性能问题是我的旧input_fn()
花了我大约13个挂钟分钟来完成20k步骤,但即使buffer_size为10,000(我认为这意味着我们要等到我们已处理10,000批次)我仍在等待超过40分钟缓冲区已满。这么长时间有意义吗?如果我知道我在GCS上的分片.csv已经被随机化了,那么这个shuffle / buffer size是否可以接受?我试图从tf.train.shuffle_batch()复制行为 - 然而,似乎在最坏的情况下它需要花费相同的13分钟才能达到10k步骤才能填满缓冲区?
步/秒
即使缓冲区已经填满,全局步数/秒也会在同一模型上达到大约3步/秒(通常低至2步/秒),前一个input_fn()得到~13步/秒。
SLOPPY INTERLEAVE
我finall尝试用sloppy_interleave()替换parallel_interleave(),因为这是@mrry的另一个建议。当我切换到sloppy_interleave时,我得到了14步/秒!我知道这意味着它不是确定性的,但这应该只是意味着从一次运行(或时代)到下一次运行(或时代)不是确定性的?或者对此有更大的影响?我应该关注旧的shuffle_batch()
方法和sloppy_interleave之间的真正区别吗?事实上,这导致4-5倍的改善表明之前的阻塞因素是什么?
答案 0 :(得分:2)
使用tf.data.TextLineDataset
时,每个元素都是标量字符串。在这方面,它更类似于使用tf.TextLineReader.read()
而不是批处理版本tf.TextLineReader.read_up_to()
,后者返回字符串向量。不幸的是tf.string_split()
op需要一个矢量输入(虽然将来可能会改变),所以当前需要进行形状处理。
查找表与tf.data
中的函数的交互方式略有不同。直觉是你应该在外 Dataset.map()
调用之后声明查找表(以便它将被初始化一次),然后在parse_csv()
函数中捕获它以调用{ {1}}。以下内容应该有效:
vocab_table.lookup()
答案 1 :(得分:2)
在TF 1.4(目前是与GCMLE配合使用的TF的最新版本)中,您将无法使用make_one_shot_iterator()
查找表(请参阅相关post),您将需要使用Dataset.make_initializable_iterator()
然后使用您的默认iterator.initalizer
(来自此post)初始化TABLES_INITIALIZER
。以下是input_fn()
的外观:
def input_fn(...):
dataset = tf.data.Dataset.list_files(filenames).shuffle(num_shards)
# Define `vocab_table` outside the map function and use it in `parse_csv()`.
vocab_table = tf.contrib.lookup.index_table_from_file(
vocabulary_file=hparams.vocab_file, default_value=0)
dataset = dataset.interleave(
lambda filename: (tf.data.TextLineDataset(filename)
.skip(1)
.map(lambda row: parse_csv(row, hparams),
num_parallel_calls=multiprocessing.cpu_count())),
cycle_length=5)
if shuffle:
dataset = dataset.shuffle(buffer_size=10000)
dataset = dataset.repeat(num_epochs)
dataset = dataset.batch(batch_size)
iterator = dataset.make_initializable_iterator()
features = iterator.get_next()
# add iterator.intializer to be handled by default table initializers
tf.add_to_collection(tf.GraphKeys.TABLE_INITIALIZERS, iterator.initializer)
labels = features.pop(LABEL_COLUMN)
return features, labels