【发布时间】:2018-08-19 21:21:12
【问题描述】:
我正在使用 Keras 来训练一个神经网络,并且我的数据集变得比我计算机上安装的 RAM 量还大,所以是时候修改我的训练脚本来调用模型了.fit_generator() 而不是 model.fit(),这样我就不必一次将所有训练和验证数据加载到 RAM 中。
我已经进行了修改,AFAICT 工作正常,但有一点让我有点困扰——我在网上看到的 fit_generator() 的所有示例用法都使用 Python 的 yield 功能存储生成器的状态。我本质上是一个老 C++ 程序员,对 yield 等我不完全理解的功能持怀疑态度,因此我想显式地而不是隐式地维护我的生成器的状态,所以我这样实现了我的生成器:
class DataGenerator:
def __init__(self, inputFileName, maxExamplesPerBatch):
self._inputFileName = inputFileName
self._maxExamplesPerBatch = maxExamplesPerBatch
self._inputsFile = open(inputFileName, "rb")
if (self._inputsFile == None):
self._print("Couldn't open file %s to read input data" % inputFileName)
sys.exit(10)
self._outputsFile = open(inputFileName, "rb") # yes, we're deliberately opening the same file twice (to avoid having to call seek() a lot)
if (self._outputsFile == None):
self._print("Couldn't open file %s to read output data" % inputFileName)
sys.exit(10)
headerInfo = struct.unpack("<4L", self._inputsFile.read(16))
if (headerInfo[0] != 1414676815):
print("Bad magic number in input file [%s], aborting!" % inputFileName)
sys.exit(10)
self._numExamples = headerInfo[1] # Number of input->output rows in our data-file (typically quite large, i.e. millions)
self._numInputs = headerInfo[2] # Number of input values in each row
self._numOutputs = headerInfo[3] # Number of output values in row
self.seekToTopOfData()
def __len__(self):
return (math.ceil(self._numExamples/self._maxExamplesPerBatch))
def __next__(self):
numExamplesToLoad = self._maxExamplesPerBatch
numExamplesLeft = self._numExamples - self._curExampleIdx
if (numExamplesLeft < numExamplesToLoad):
numExamplesToLoad = numExamplesLeft
inputData = np.reshape(np.fromfile(self._inputsFile, dtype='<f4', count=(numExamplesToLoad*self._numInputs)), (numExamplesToLoad, self._numInputs))
outputData = np.reshape(np.fromfile(self._outputsFile, dtype='<f4', count=(numExamplesToLoad*self._numOutputs)), (numExamplesToLoad, self._numOutputs))
self._curExampleIdx += numExamplesToLoad
if (self._curExampleIdx == self._numExamples):
self.seekToTopOfData()
return (inputData, outputData) # <----- NOTE return, not yield!!
def seekToTopOfData(self):
self._curExampleIdx = 0
self._inputsFile.seek(16)
self._outputsFile.seek(16+(self._numExamples*self._numInputs*4))
[...]
trainingDataGenerator = DataGenerator(trainingInputFileName, maxExamplesPerBatch)
validationDataGenerator = DataGenerator(validationInputFileName, maxExamplesPerBatch)
model.fit_generator(generator=trainingDataGenerator, steps_per_epoch=len(trainingDataGenerator), epochs=maxEpochs, callbacks=callbacks_list, validation_data=validationDataGenerator, validation_steps=len(validationDataGenerator))
...请注意,我的 __next__(self) 函数以 return 而不是 yield 结尾,并且我明确存储生成器的状态(通过 DataGenerator 对象中的私有成员变量)而不是隐式(通过yield魔法)。这似乎工作正常。
我的问题是,这种不寻常的方法会引入我应该注意的任何不明显的行为问题吗?
【问题讨论】: