【问题标题】:Is it reasonable to implement a "generator" for Keras' fit_generator method() without calling yield()?在不调用 yield() 的情况下为 Keras 的 fit_generator method() 实现“生成器”是否合理?
【发布时间】: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魔法)。这似乎工作正常。

我的问题是,这种不寻常的方法会引入我应该注意的任何不明显的行为问题吗?

【问题讨论】:

    标签: python keras generator


    【解决方案1】:

    对您的代码进行粗略检查。当您编写一个生成器函数并调用它时,调用会返回一个生成器,其 __next__ 方法通常会被迭代重复调用,直到它引发 StopIteration 异常。

    生成器是迭代器的一种特殊情况。像列表这样的可迭代对象有一个 __iter__ 方法来生成一个迭代器。

    除非您想将值发送到您的生成器并从中取出,否则您的DataGenerator 是实现迭代器的合理方式,但要编写可迭代对象,您需要另一个类其__iter__ 方法返回DataGenerator 的实例。

    How to implement __iter__(self) for a container object (Python) 的答案也可能会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-16
      • 1970-01-01
      • 2018-07-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-04
      • 2018-11-23
      相关资源
      最近更新 更多