【问题标题】:Using multi-output labels in keras ImageDataGenerator.flow() and using model.fit_generator()在 keras ImageDataGenerator.flow() 中使用多输出标签并使用 model.fit_generator()
【发布时间】:2020-01-05 19:17:43
【问题描述】:

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

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-labels 如下

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_crossentropy 还是sparse_categorical_crossentropy?对于下面给出的代码,我应该如何在流程中使用Y_labels

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

【问题讨论】:

  • 作为任何 numpy 数组,您将传递给普通的 fit 方法。
  • 您可能需要一个具有三个输出的模型,每个输出都有一个'softmax' 和一个'sparse_categorical_crossentropy',并且您可能不需要更改数组上的任何内容。它只需要与图像的顺序相同。
  • @DanielMöller 你的意思是一个 3D、one-hot 编码的数组?
  • 我的意思是三个输出张量,所有三个张量都是二维的,与分类分类模型的任何其他二维完全相同。如果您使用数字标签,请使用三个 'sparse_categorical_crossentropy' 损失。如果使用 one-hot 标签,则使用三个 'categorical_crossentropy' loss。
  • 使用y = [y1, y2, y3]

标签: python keras neural-network conv-neural-network


【解决方案1】:

哎哟……

根据您的flow 中给出的消息,您将需要一个输出。因此,您需要在模型内部进行分离。 (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_loss 编译模型。

那么当您使用flow 时,flow 应该停止抱怨。

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

【讨论】:

  • 天哪!非常感谢。我的头简直快要爆炸了。我使用了您刚才建议我的完全相同的东西,但没有自定义损失。 flow 运行顺利,但模型要求 3 个单独的 y_labels。
  • 好奇心:我正在训练一个输出与你的非常相似的模型。 3组分类类别。我注意到某些类可能比其他类更早开始过度拟合,并且由于类数量不同,损失并不完全平衡。但是我仍然不知道平衡损失的最佳方法是什么,我问了这个问题:datascience.stackexchange.com/questions/64205/…
  • 你能告诉我解决我最新问题之一的方法吗? flow 中没有 target_size。如何将我的 images_arr(samples,137,236,1) 调整为 (samples,64,64,1)
  • 您需要构建自己的生成器....参见keras.io/utils中的Sequence
  • (我必须走了,对不起)
【解决方案2】:

另一种解决方法是:

  1. 创建一个元组数组,然后将其传递给 ImageDataGenerator 流方法。
  2. 创建一个迭代器方法,该方法接受上一步创建的迭代器。此迭代器将元组数组转换回数组列表。

以下是实现上述步骤的方法:

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_train的大小和X_train一样,应该可以接受。 'train_image_flow' 返回数组列表 应该被 Keras 多输出模型所接受。

添加(2019/01/26)

另一种思路,比上面那个简单:

  1. 将包含 0、1、2、... 的索引数组传递给 ImageDataGenerator.flow()。
  2. 在迭代器中,使用原始流中返回的索引数组选择数组中的元素以进行多输出。

这里是实现:

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)

【讨论】:

    猜你喜欢
    • 2018-05-15
    • 1970-01-01
    • 1970-01-01
    • 2018-03-11
    • 2017-08-26
    • 1970-01-01
    • 2020-03-27
    • 2019-02-20
    • 1970-01-01
    相关资源
    最近更新 更多