首页 > 解决方案 > 是否可以使用自定义生成器来训练带有 keras tensorflow 2.0.0 的多输入架构?

问题描述

使用 TF 2.0.0,我可以训练一个输入的架构,我可以使用自定义生成器训练一个输入的架构,我可以训练一个有两个输入的架构。但是我无法使用自定义生成器训练具有两个输入的架构。

为了保持简约,这里有一个简单的例子,没有生成器,也没有多个输入:

from tensorflow.keras import layers, models, Model, Input, losses
from numpy import random, array, zeros

input1 = Input(shape=2)
dense1 = layers.Dense(5)(input1)
fullModel = Model(inputs=input1, outputs=dense1)
fullModel.summary()

# Generate random examples:
nbSamples = 21
X_train = random.rand(nbSamples, 2)
Y_train = random.rand(nbSamples, 5)

batchSize = 4
fullModel.compile(loss=losses.LogCosh())
fullModel.fit(X_train, Y_train, epochs=10, batch_size=batchSize)

model.fit()这是一个简单的密集层,它接受大小为 2 的输入向量。随机生成的数据集包含 21 个示例,批量大小为 4。我们也可以在输入中提供自定义生成器,而不是加载所有数据并将它们提供给 。这样做的主要优点(对于 RAM 消耗)是仅批量加载而不是整个数据集。这是一个使用先前架构和自定义生成器的简单示例:

import json
# Save the last dataset in a file:
with open("./dataset1input.txt", 'w') as file:
    for i in range(nbSamples):
        example = {"x": X_train[i].tolist(), "y": Y_train[i].tolist()}
        file.write(json.dumps(example) + "\n")

def generator1input(datasetPath, batch_size, inputSize, outputSize):
    X_batch = zeros((batch_size, inputSize))
    Y_batch = zeros((batch_size, outputSize))
    i=0
    while True:
        with open(datasetPath, 'r') as file:
            for line in file:
                example = json.loads(line)
                X_batch[i] = array(example["x"])
                Y_batch[i] = array(example["y"])
                i+=1
                if i % batch_size == 0:
                    yield (X_batch, Y_batch)
                    i=0

fullModel.compile(loss=losses.LogCosh())
my_generator = generator1input("./dataset1input.txt", batchSize, 2, 5)
fullModel.fit(my_generator, epochs=10, steps_per_epoch=int(nbSamples/batchSize))

在这里,生成器打开数据集文件,但每次调用时只加载 batch_size 示例(不是 nbSamples 示例),并在循环时滑入文件中。

现在,我可以构建一个具有 2 个输入且没有生成器的简单功能架构:

input1 = Input(shape=2)
dense1 = layers.Dense(5)(input1)
subModel1 = Model(inputs=input1, outputs=dense1)
input2 = Input(shape=3)
dense2 = layers.Dense(5)(input2)
subModel2 = Model(inputs=input2, outputs=dense2)
averageLayer = layers.average([subModel1.output, subModel2.output])
fullModel = Model(inputs=[input1, input2], outputs=averageLayer)
fullModel.summary()

# Generate random examples:
nbSamples = 21
X1 = random.rand(nbSamples, 2)
X2 = random.rand(nbSamples, 3)
Y = random.rand(nbSamples, 5)

fullModel.compile(loss=losses.LogCosh())
fullModel.fit([X1, X2], Y, epochs=10, batch_size=batchSize)

直到这里,所有模型都编译并运行,但我无法使用具有最后一个架构及其 2 个输入的生成器......通过尝试以下代码(我认为这在逻辑上应该有效):

# Save data in a file:
with open("./dataset.txt", 'w') as file:
    for i in range(nbSamples):
        example = {"x1": X1[i].tolist(), "x2": X2[i].tolist(), "y": Y[i].tolist()}
        file.write(json.dumps(example) + "\n")

def generator(datasetPath, batch_size, inputSize1, inputSize2, outputSize):
    X1_batch = zeros((batch_size, inputSize1))
    X2_batch = zeros((batch_size, inputSize2))
    Y_batch = zeros((batch_size, outputSize))
    i=0
    while True:
        with open(datasetPath, 'r') as file:
            for line in file:
                example = json.loads(line)
                X1_batch[i] = array(example["x1"])
                X2_batch[i] = array(example["x2"])
                Y_batch[i] = array(example["y"])
                i+=1
                if i % batch_size == 0:
                    yield ([X1_batch, X2_batch], Y_batch)
                    i=0

fullModel.compile(loss=losses.LogCosh())
my_generator = generator("./dataset.txt", batchSize, 2, 3, 5)
fullModel.fit(my_generator, epochs=10, steps_per_epoch=(nbSamples//batchSize))

我收到以下错误:

File "C:\Anaconda\lib\site-packages\tensorflow_core\python\keras\engine\training.py", line 729, in fit
    use_multiprocessing=use_multiprocessing)
File "C:\Anaconda\lib\site-packages\tensorflow_core\python\keras\engine\training_v2.py", line 224, in fit
    distribution_strategy=strategy)
File "C:\Anaconda\lib\site-packages\tensorflow_core\python\keras\engine\training_v2.py", line 547, in _process_training_inputs
    use_multiprocessing=use_multiprocessing)
File "C:\Anaconda\lib\site-packages\tensorflow_core\python\keras\engine\training_v2.py", line 606, in _process_inputs
    use_multiprocessing=use_multiprocessing)
File "C:\Anaconda\lib\site-packages\tensorflow_core\python\keras\engine\data_adapter.py", line 566, in __init__
    reassemble, nested_dtypes, output_shapes=nested_shape)
File "C:\Anaconda\lib\site-packages\tensorflow_core\python\data\ops\dataset_ops.py", line 540, in from_generator
    output_types, tensor_shape.as_shape, output_shapes)
File "C:\Anaconda\lib\site-packages\tensorflow_core\python\data\util\nest.py", line 471, in map_structure_up_to
    results = [func(*tensors) for tensors in zip(*all_flattened_up_to)]
File "C:\Anaconda\lib\site-packages\tensorflow_core\python\data\util\nest.py", line 471, in <listcomp>
    results = [func(*tensors) for tensors in zip(*all_flattened_up_to)]
File "C:\Anaconda\lib\site-packages\tensorflow_core\python\framework\tensor_shape.py", line 1216, in as_shape
    return TensorShape(shape)
File "C:\Anaconda\lib\site-packages\tensorflow_core\python\framework\tensor_shape.py", line 776, in __init__
    self._dims = [as_dimension(d) for d in dims_iter]
File "C:\Anaconda\lib\site-packages\tensorflow_core\python\framework\tensor_shape.py", line 776, in <listcomp>
    self._dims = [as_dimension(d) for d in dims_iter]
File "C:\Anaconda\lib\site-packages\tensorflow_core\python\framework\tensor_shape.py", line 718, in as_dimension
    return Dimension(value)
File "C:\Anaconda\lib\site-packages\tensorflow_core\python\framework\tensor_shape.py", line 193, in __init__
    self._value = int(value)
TypeError: int() argument must be a string, a bytes-like object or a number, not 'tuple'

正如文档中解释的那样,x参数model.fit()可以是A generator or keras.utils.Sequence returning (inputs, targets)The iterator should return a tuple of length 1, 2, or 3, where the optional second and third elements will be used for y and sample_weight respectively。因此,我认为它不能接受超过一台发电机的输入。自定义生成器可能无法进行多个输入。请问,你能解释一下吗?一个办法?

(否则,似乎可以tf.data.Dataset.from_generator()采用不那么定制的方法,但我很难理解output_signature论点中要指出的内容)



[编辑]感谢您的回复@Francis Tang。事实上,可以使用自定义生成器,但它让我明白我只需要更改行:

yield ([X1_batch, X2_batch], Y_batch)

到:

yield (X1_batch, X2_batch), Y_batch

尽管如此,使用tf.keras.utils.Sequence. 但我觉得它有点限制。特别是,我在给出的示例(以及我可以找到的大多数示例中Sequence)中理解,__init__()它首先用于加载完整的数据集,这违背了生成器的利益。但也许这是一个关于 Sequence() 的特殊示例,没有必要这样使用__init__():您可以直接读取文件并将所需的批处理加载到__getitem__(). 在这种情况下,似乎每次都推送浏览数据文件,否则需要事先为每个批次创建一个文件(不是真正优化的)。

标签: python-3.xkerasgeneratortensorflow2.0

解决方案


from tensorflow.python.keras.utils.data_utils import Sequence

class generator(Sequence):
    def __init__(self,filename,batch_size):
        data = pickle.load(open(filename,'rb'))
        self.X1 = data['X1']
        self.X2 = data['X2']
        self.y = data['y']
        self.bs = batch_size
    
    def __len__(self):
        return (len(self.y) - 1) // self.bs + 1
    
    def __getitem__(self,idx):
        start, end = idx * self.bs, (idx+1) * self.bs
        return (self.X1[start:end], self.X2[start:end]), self.y[start:end]

您需要使用 Sequence 编写一个类:https ://www.tensorflow.org/api_docs/python/tf/keras/utils/Sequence


推荐阅读