我的mxnet脚本可能受到将数据加载到GPU的I / O的限制,我正在尝试通过预取来加快速度。问题是我不知道如何使用自定义数据迭代器进行预取。
我的第一个假设/希望是,设置self.preprocess_threads和self.prefetch_buffer的值就足够了,就像我看到的here这样的迭代器(例如{{1 }}。但是,当我这样做时,在设置这些变量之前,我发现相对于脚本而言,性能没有任何变化,因此显然设置这些设置无效。
然后我注意到,除了我为其实现了子类mxnet.io.ImageRecordUInt8Iter
的基类之外,还存在类mx.io.PrefetchingIter
。我找到了这个documentation,但是我找不到任何示例,对于何时何地需要发生什么我有些困惑。但是,我不清楚如何使用它。例如。我看到除了mx.io.DataIter
之外,它还有一个next()
方法,该方法只是说“移至下一批”。这到底是什么意思?不生产就“移动”到下一批是什么意思?我发现了该类的source code,并通过简短的阅读,似乎需要多个迭代器并为每个迭代器创建一个线程。对于我当前的设计,这可能不起作用,因为我确实希望使用多个线程从同一个迭代器中预取。
这是我要通过自定义数据迭代器执行的操作
iter_next()
,在该数据可用时我会在其上弹出数据multiprocessing.Queue
)命令行脚本来生成该数据,该脚本执行一个c ++二进制文件,该文件会生成一个multiprocessing
文件numpy
文件并将其内容加载到内存中,对其进行处理,然后将处理后的位放在全局numpy
上这是我的代码:
multiprocessing.Queue
然后我尝试制作这些迭代器之一以及像这样的预取迭代器:
def launchJobForDate(date_str):
### this is a function that gets called via multiprocessing
### to produce new data by calling a c++ binary
### whenever data queue is empty so that we need to produce more data
try:
f = "testdata/data%s.npy"%date_str
if not os.path.isfile(f):
cmd = CMD % ( date_str, JSON_FILE, date_str, date_str, date_str)
while True:
try:
output = subprocess.check_output(cmd, shell=True)
break
except:
pass
while True:
try:
d = np.load(f)
break
except:
pass
data_queue.put((d, date_str))
except Exception as ex:
print("launchJobForDate: ERROR ", ex)
class ProduceDataIter(mx.io.DataIter):
@staticmethod
def processData(d, time_steps, num_inputs):
try:
...processes data...
return [z for z in zip(bigX, bigY, bigEvalY, dates)]
except Exception as ex:
print("processData: ERROR ", ex)
def __init__(self, num_mgrs, end_date_str):
## iter stuff
self.preprocess_threads = 4
self.prefetch_buffer = 1
## set up internal data to preserve state
## and make a list of dates for which to run binary
@property
def provide_data(self):
return [mx.io.DataDesc(name='seq_var',
shape=(args_batch_size * GPU_COUNT,
self.time_steps,
self.num_inputs),
layout='NTC')]
@property
def provide_label(self):
return [mx.io.DataDesc(name='bd_return',
shape=(args_batch_size * GPU_COUNT)),
mx.io.DataDesc(name='bd_return',
shape=(args_batch_size * GPU_COUNT, num_y_cols)),
mx.io.DataDesc(name='date',
shape=(args_batch_size * GPU_COUNT))]
def __next__(self):
try:
z = self.z.pop(0)
data = z[0:1]
label = z[1:]
return mx.io.DataBatch(data, label)
except Exception as ex:
### if self.z (a list) has no elements to pop we need
### to get more data off the queue, process it, and put it
### on self.x so it's ready for calls to __next__()
while True:
try:
d = data_queue.get_nowait()
processedData = ProduceDataIter.processData(d,
self.time_steps,
self.num_inputs)
self.z.extend(processedData)
counter_queue.put(counter_queue.get() - 1)
z = self.z.pop(0)
data = z[0:1]
label = z[1:]
return mx.io.DataBatch(data, label)
except queue.Empty:
...this is where new jobs to produce new data and put them
...on the queue would happen if nothing is left on the queue
问题在于,mgr = ProcessMgr(2, end_date_str)
mgrOuter = mx.io.PrefetchingIter([mgr])
会在第一次调用mgrOuter
时立即抛出StopIteration
,而不会像我想的那样调用__next__()
。>
最后,我还注意到mgr.__next__()
有一个seems like it might handle prefetching的gluon
对象,但是在这种情况下,它似乎还假设基础数据来自DataLoader
其布局是有限且不变的(基于它是根据Dataset
实现的,该索引带有一个索引)。因此,考虑到我作为训练输入生成的数据的动态类队列性质,我似乎并没有追求这种选择。
我的问题是:
感谢您的反馈和建议。
答案 0 :(得分:0)
正如您已经提到的,gluon DataLoader提供了预取。在自定义DataIterator中,您将Numpy数组用作输入。因此,您可以执行以下操作:
f = "testdata/data%s.npy"%date_str
data = np.load(f)
train = gluon.data.ArrayDataset(mx.nd.array(data))
train_iter = gluon.data.DataLoader(train, shuffle=True, num_workers=4, batch_size=batch_size, last_batch='rollover')
由于您是动态创建数据的,因此可以尝试在每个时期重置DataLoader并加载新的Numpy数组。 如果GPU利用率仍然很低,请尝试增加batch_size和num_workers。另一个问题也可能是数据集的大小。重置DataLoader会影响性能,因此拥有更大的数据集会增加一个纪元的时间,从而提高性能。