【问题标题】:Keras: model with one input and two outputs, trained jointly on different data (semi-supervised learning)Keras:一个输入和两个输出的模型,在不同的数据上联合训练(半监督学习)
【发布时间】:2017-11-01 21:25:50
【问题描述】:

我想用 Keras 编写一个神经网络,它既可以用作自动编码器,也可以用作半监督学习的分类器。以这个数据集为例,其中有少量标记图像和大量未标记图像:https://cs.stanford.edu/~acoates/stl10/

here 列出的一些论文成功地实现了这一点,或者非常相似的事情。

总结一下:如果模型具有相同的输入数据形状和相同的“编码”卷积层,但会分成两个头(叉式),那么就有一个分类头和一个解码头,在无监督自动编码器将有助于分类头的良好学习的一种方式。

使用 TensorFlow,这样做不会有任何问题,因为我们可以完全控制计算图。

但是对于 Keras,事情更高级,我觉得所有对“.fit”的调用必须始终同时提供所有数据(所以它会迫使我将分类头和自动编码头捆绑在一起一个时间步长)。

在 keras 中几乎可以做到这一点的一种方法是使用这样的东西:

input = Input(shape=(32, 32, 3))
cnn_feature_map = sequential_cnn_trunk(input)

classification_predictions = Dense(10, activation='sigmoid')(cnn_feature_map)

autoencoded_predictions = decode_cnn_head_sequential(cnn_feature_map)

model = Model(inputs=[input], outputs=[classification_predictions, ])

model.compile(optimizer='rmsprop',
              loss='binary_crossentropy',
              metrics=['accuracy'])
model.fit([images], [labels, images], epochs=10)

但是,我认为并且我担心如果我只是想以这种方式安装东西,它会失败并要求丢失的头部:

for epoch in range(10):
    # classifications step
    model.fit([images], [labels, None], epochs=1)
    # "semi-unsupervised" autoencoding step
    model.fit([images], [None, images], epochs=1)
    # note: ".train_on_batch" could probably be used rather than ".fit" to avoid doing a whole epoch each time.

应该如何使用 Keras 实现这一行为?是否可以联合进行训练而不必将两个调用拆分为“.fit”函数?

【问题讨论】:

    标签: machine-learning computer-vision deep-learning keras unsupervised-learning


    【解决方案1】:

    有时当您没有标签时,您可以传递零向量而不是一个热编码向量。它不应该改变你的结果,因为零向量没有任何带有分类交叉熵损失的误差信号。

    我的自定义 to_categorical 函数如下所示:

    def tricky_to_categorical(y, translator_dict):
        encoded = np.zeros((y.shape[0], len(translator_dict)))
        for i in range(y.shape[0]):
            if y[i] in translator_dict:
                encoded[i][translator_dict[y[i]]] = 1
        return encoded
    

    当 y 包含标签时,translator_dict 是一个 Python 字典,它包含标签及其唯一键,如下所示:

    {'unisex':2, 'female': 1, 'male': 0}

    如果在此字典中找不到 UNK 标签,则其编码标签将为零向量

    如果你使用这个技巧,你还必须修改你的准确度函数才能看到真实的准确度数字。您必须从我们的指标中过滤掉所有零向量

    def tricky_accuracy(y_true, y_pred):
        mask = K.not_equal(K.sum(y_true, axis=-1), K.constant(0))  # zero vector mask
        y_true = tf.boolean_mask(y_true, mask)
        y_pred = tf.boolean_mask(y_pred, mask)
        return K.cast(K.equal(K.argmax(y_true, axis=-1), K.argmax(y_pred, axis=-1)), K.floatx())
    

    注意:您必须使用更大的批次(例如 32 个)以防止零矩阵更新,因为它会使您的准确度指标变得疯狂,我不知道为什么

    替代解决方案

    使用伪标签:)

    【讨论】:

    • 我不明白你在底部的意思(关于批次)。您是说在给定批次中,每个项目都必须没有标签(0 掩码)还是批次中的所有项目都必须有标签?
    • 一些“one hot”编码标签可以是零向量(因为我们不知道正确的标签)。我们不想要一个更新,其中所有标签都是批处理中的零向量(0 矩阵),因为我不知道为什么它会导致 keras 中的一些度量问题。至少有一个标签必须有信息,不应该是零向量
    【解决方案2】:

    你可以联合训练,你必须传递一个包含单个标签的数组。

    我使用了 fit_generator,例如

    model.fit_generator(
    batch_generator(),
    steps_per_epoch=len(dataset) / batch_size,
    epochs=epochs)
    
    
    def batch_generator():
    batch_x = np.empty((batch_size, img_height, img_width, 3))
    gender_label_batch = np.empty((batch_size, len(gender_dict)))
    category_label_batch = np.empty((batch_size, len(category_dict)))
    while True:
        i = 0
        for idx in np.random.choice(len(dataset), batch_size):
            image_id = dataset[idx][0]
            batch_x[i] = load_and_convert_image(image_id)
            gender_label_batch[i] = gender_labels[idx]
            category_label_batch[i] = category_labels[idx]
    
            i += 1
        yield batch_x, [gender_label_batch, category_label_batch]
    

    【讨论】:

    • 问题是我不能同时访问这两个标签。例如,对于 X,我将只有 Y_1,而对于另一个 X,我将同时拥有 Y_1 和 Y_2。但问题是我不能总是同时给出两个输出。无论如何,如果我打算这样做,最终将使用另一个库。
    • 现在我看到了你的问题,别担心我也有一个(自己的)解决方案:)你需要一个自定义的 to_categorical 和一个自定义的准确度函数。明天我将发布一个详细的答案,主要思想是当你没有标签时,你应该传递一个热向量的零向量。在这种情况下,您没有任何错误信号,仅此而已。
    猜你喜欢
    • 1970-01-01
    • 2011-11-09
    • 2019-09-11
    • 2019-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-08
    • 2017-12-10
    相关资源
    最近更新 更多