Python 在keras ImageDataGenerator.flow()中使用多个输出标签,并使用model.fit_generator()

Python 在keras ImageDataGenerator.flow()中使用多个输出标签,并使用model.fit_generator(),python,keras,neural-network,conv-neural-network,Python,Keras,Neural Network,Conv Neural Network,我有一个单输入多输出的神经网络模型,它的最后一层是 out1 = Dense(168, activation = 'softmax')(dense) out2 = Dense(11, activation = 'softmax')(dense) out3 = Dense(7, activation = 'softmax')(dense) model = Model(inputs=inputs, outputs=[out1,out2,out3]) 每个图像的Y标签如下所示 train >

我有一个单输入多输出的神经网络模型,它的最后一层是

out1 = Dense(168, activation = 'softmax')(dense)
out2 = Dense(11, activation = 'softmax')(dense)
out3 = Dense(7, activation = 'softmax')(dense)

model = Model(inputs=inputs, outputs=[out1,out2,out3])

每个图像的Y标签如下所示

train
>>

              image_id    class_1   class_2  class_3    

0              Train_0         15         9        5    
1              Train_1        159         0        0
...
...
...
453651    Train_453651          0        15       34
453652    Train_453652         18         0        7
编辑:-

train.iloc[:,1:4].nunique()
>>
class_1        168
class_2         11
class_3          7
dtype: int64
那么看看这些不同的类,我应该使用
categorical\u crossentropy
还是
sparse\u categorical\u crossentropy
?对于下面给出的代码,我应该如何在流程中使用
Y_标签

imgs_arr = df.iloc[:,1:].values.reshape(df.shape[0],137,236,1)
# 32332 columns representing pixels of 137*236 and single channel images.
# converting it to (samples,w,h,c) format

Y = train.iloc[:,1:].values #need help from here

image_data_gen = ImageDataGenerator(validation_split=0.25)
train_gen = image_data_gen.flow(x=imgs_arr, y=Y, batch_size=32,subset='training')
valid_gen = image_data_gen.flow(x=imgs_arr,y=Y,subset='validation')
这是传递
Y
还是使用
Y=[y1,y2,y3]
的正确方法

y1=train.iloc[:,1].values
y2=train.iloc[:,2].values
y3=train.iloc[:,3].values

哎哟

根据
流中给出的消息
,您将需要一个输出。因此,您需要在模型内部进行分离。(Keras未能遵循其自身的标准)

这意味着:

Y = train.iloc[:,1:].values #shape = (50210, 3)
具有单个输出,如:

out = Dense(168+11+7, activation='linear')(dense)
以及处理分离的损失函数:

def custom_loss(y_true, y_pred):
    true1 = y_true[:,0:1]
    true2 = y_true[:,1:2]
    true3 = y_true[:,2:3]

    out1 = y_pred[:,0:168]
    out2 = y_pred[:,168:168+11]
    out3 = y_pred[:,168+11:]

    out1 = K.softmax(out1, axis=-1)
    out2 = K.softmax(out2, axis=-1)
    out3 = K.softmax(out3, axis=-1)

    loss1 = K.sparse_categorical_crossentropy(true1, out1, from_logits=False, axis=-1)
    loss2 = K.sparse_categorical_crossentropy(true2, out2, from_logits=False, axis=-1)
    loss3 = K.sparse_categorical_crossentropy(true3, out3, from_logits=False, axis=-1)

    return loss1+loss2+loss3
使用
loss=custom\u loss
编译模型

然后当您执行
flow
时,
flow
应该停止抱怨

只需确保X和Y的顺序完全相同:
imgs\u arr[i]
正确对应于
Y[i]

另一个解决方法是:

  • 创建一个元组数组,然后将其传递给ImageDataGenerator流方法
  • 生成一个迭代器方法,该方法接受上一步生成的迭代器。此迭代器将元组数组转换回数组列表
  • 以下是实现上述步骤的方法:

    def make_array_of_tuple(tuple_of_arrays):
        array_0 = tuple_of_arrays[0]
        array_of_tuple = np.empty(array_0.shape[0], dtype=np.object)
        for i, tuple_of_array_elements in enumerate(zip(*tuple_of_arrays)):
            array_of_tuple[i] = tuple_of_array_elements
        return array_of_tuple
    
    def convert_to_list_of_arrays(array_of_tuple):
        array_length = array_of_tuple.shape[0]
        tuple_length = len(array_of_tuple[0])
        array_list = [
            np.empty(array_length, dtype=np.uint8) for i in range(tuple_length) ]
        for i, array_element_tuple in enumerate(array_of_tuple):
            for array, tuple_element in zip(array_list, array_element_tuple):
                array[i] = tuple_element
        return array_list
    
    def tuple_of_arrays_flow(original_flow):
        while True:
            (X, array_of_tuple) = next(original_flow)
            list_of_arrays = convert_to_list_of_arrays(array_of_tuple)
            yield X, list_of_arrays
    
    要调用ImageDataGenerator flow()方法并获取用于模型的流,请执行以下操作:

    y_train = make_array_of_tuple((y_train_1, y_train_2, y_train_3))
    orig_image_flow = train_image_generator.flow(X_train, y=y_train)
    train_image_flow = tuple_of_arrays_flow(orig_image_flow)
    
    y_列车的尺寸与X_列车相同,因此应予以接受。 “train\u image\u flow”返回数组列表 这应该为Keras多输出模型所接受

    新增(2019/01/26)

    一个是另一个想法,比上面的想法简单:

  • 将索引数组(包含0、1、2,…)传递给ImageDataGenerator.flow()
  • 在迭代器中,使用从原始流返回的索引数组,为多个输出选择数组中的元素
  • 以下是实施方案:

    def make_multi_output_flow(image_gen, X, y_list, batch_size):
        y_item_0 = y_list[0]
        y_indices = np.arange(y_item_0.shape[0])
        orig_flow = image_gen.flow(X, y=y_indices, batch_size=batch_size)
    
        while True:
            (X, y_next_i) = next(orig_flow)
            y_next = [ y_item[y_next_i] for y_item in y_list ]
            yield X, y_next
    
    这是一个调用上述方法的示例

    y_train = [y_train_1, y_train_2, y_train_3]
    multi_output_flow = make_multi_output_flow(
        image_data_generator, X_train, y_train, batch_size)
    

    与任何numpy数组一样,您将传递到一个普通的
    fit
    方法。您可能需要一个具有三个输出的模型,每个输出具有
    'softmax'
    'sparse\u categorical\u crossentropy'
    ,并且您可能不需要更改y数组上的任何内容。它只需要与图像的顺序相同。@DanielMöller你是说一个3D,一个热编码数组?我是说三个输出张量,所有三个张量都是2D,与分类分类模型的任何其他2D一样。如果您使用数字标签,请使用三个
    'sparse\u categorical\u crossentropy'
    loss。如果你用一个热标签,用三个“分类交叉熵”损失。用
    y=[y1,y2,y3]
    哦,天哪!谢谢。我的头几乎要爆炸了。我使用了与你刚才建议的完全相同的东西,但没有自定义损失<代码>流程运行平稳,但模型要求3个单独的y_标签。好奇:我正在训练一个输出与您非常相似的模型。3组分类类。我注意到某些类别可能比其他类别更早开始过度拟合,并且由于类别数量不同,损失并没有完全平衡。但我仍然不知道平衡损失的最佳方法是什么,我问了一个问题:你能告诉我一个最新问题的解决方案吗。
    中没有目标大小。如何将我的
    图像从
    (示例,137236,1)
    调整为
    (示例,64,64,1)
    ?您需要构建自己的生成器。。。。请参见中的
    序列
    (对不起,我必须走了)