【问题标题】:How to use keras ImageDataGenerator with a Siamese or Tripple networks如何将 keras ImageDataGenerator 与连体或三重网络一起使用
【发布时间】:2017-05-28 17:39:12
【问题描述】:

我正在尝试在自定义大型数据集上同时构建 Siamese 神经网络和 triple neural network

Keras 具有ImageDataGenerator,这使得向regular 神经网络生成输入数据非常容易。

我很高兴使用ImageDataGenerator 或类似方式来训练具有 2(siamese) 和 3(triple) 输入的网络。

在mniset keras siamese example 中,由create_pairs 方法完成的预处理阶段生成的输入。我认为这种方式不适合大型数据集。

在这种情况下可以使用ImageDataGenerator 吗?假设数据集非常大,我还有哪些其他选择?

【问题讨论】:

    标签: neural-network keras keras-layer


    【解决方案1】:

    DataGenerators 的想法是给fit_generator 分批提供数据流。因此,您可以控制如何生成数据,即是从文件加载还是像所做的那样进行一些数据扩充在ImageDataGenerator。

    我在这里发布了带有自定义 DataGenerator 的 mniset siamese 示例的修改版本,您可以从这里解决。

    import numpy as np
    np.random.seed(1337)  # for reproducibility
    
    import random
    from keras.datasets import mnist
    from keras.models import Sequential, Model
    from keras.layers import Dense, Dropout, Input, Lambda
    from keras.optimizers import SGD, RMSprop
    from keras import backend as K
    
    class DataGenerator(object):
        """docstring for DataGenerator"""
        def __init__(self, batch_sz):
            # the data, shuffled and split between train and test sets
            (X_train, y_train), (X_test, y_test) = mnist.load_data()
            X_train = X_train.reshape(60000, 784)
            X_test = X_test.reshape(10000, 784)
            X_train = X_train.astype('float32')
            X_test = X_test.astype('float32')
            X_train /= 255
            X_test /= 255
    
            # create training+test positive and negative pairs
            digit_indices = [np.where(y_train == i)[0] for i in range(10)]
            self.tr_pairs, self.tr_y = self.create_pairs(X_train, digit_indices)
    
            digit_indices = [np.where(y_test == i)[0] for i in range(10)]
            self.te_pairs, self.te_y = self.create_pairs(X_test, digit_indices)
    
            self.tr_pairs_0 = self.tr_pairs[:, 0]
            self.tr_pairs_1 = self.tr_pairs[:, 1]
            self.te_pairs_0 = self.te_pairs[:, 0]
            self.te_pairs_1 = self.te_pairs[:, 1]
    
            self.batch_sz = batch_sz
            self.samples_per_train  = (self.tr_pairs.shape[0]/self.batch_sz)*self.batch_sz
            self.samples_per_val    = (self.te_pairs.shape[0]/self.batch_sz)*self.batch_sz
    
    
            self.cur_train_index=0
            self.cur_val_index=0
    
        def create_pairs(self, x, digit_indices):
            '''Positive and negative pair creation.
            Alternates between positive and negative pairs.
            '''
            pairs = []
            labels = []
            n = min([len(digit_indices[d]) for d in range(10)]) - 1
            for d in range(10):
                for i in range(n):
                    z1, z2 = digit_indices[d][i], digit_indices[d][i+1]
                    pairs += [[x[z1], x[z2]]]
                    inc = random.randrange(1, 10)
                    dn = (d + inc) % 10
                    z1, z2 = digit_indices[d][i], digit_indices[dn][i]
                    pairs += [[x[z1], x[z2]]]
                    labels += [1, 0]
            return np.array(pairs), np.array(labels)
    
        def next_train(self):
            while 1:
                self.cur_train_index += self.batch_sz
                if self.cur_train_index >= self.samples_per_train:
                    self.cur_train_index=0
                yield ([    self.tr_pairs_0[self.cur_train_index:self.cur_train_index+self.batch_sz], 
                            self.tr_pairs_1[self.cur_train_index:self.cur_train_index+self.batch_sz]
                        ],
                        self.tr_y[self.cur_train_index:self.cur_train_index+self.batch_sz]
                    )
    
        def next_val(self):
            while 1:
                self.cur_val_index += self.batch_sz
                if self.cur_val_index >= self.samples_per_val:
                    self.cur_val_index=0
                yield ([    self.te_pairs_0[self.cur_val_index:self.cur_val_index+self.batch_sz], 
                            self.te_pairs_1[self.cur_val_index:self.cur_val_index+self.batch_sz]
                        ],
                        self.te_y[self.cur_val_index:self.cur_val_index+self.batch_sz]
                    )
    
    def euclidean_distance(vects):
        x, y = vects
        return K.sqrt(K.sum(K.square(x - y), axis=1, keepdims=True))
    
    
    def eucl_dist_output_shape(shapes):
        shape1, shape2 = shapes
        return (shape1[0], 1)
    
    
    def contrastive_loss(y_true, y_pred):
        '''Contrastive loss from Hadsell-et-al.'06
        http://yann.lecun.com/exdb/publis/pdf/hadsell-chopra-lecun-06.pdf
        '''
        margin = 1
        return K.mean(y_true * K.square(y_pred) + (1 - y_true) * K.square(K.maximum(margin - y_pred, 0)))
    
    
    def create_base_network(input_dim):
        '''Base network to be shared (eq. to feature extraction).
        '''
        seq = Sequential()
        seq.add(Dense(128, input_shape=(input_dim,), activation='relu'))
        seq.add(Dropout(0.1))
        seq.add(Dense(128, activation='relu'))
        seq.add(Dropout(0.1))
        seq.add(Dense(128, activation='relu'))
        return seq
    
    
    def compute_accuracy(predictions, labels):
        '''Compute classification accuracy with a fixed threshold on distances.
        '''
        return labels[predictions.ravel() < 0.5].mean()
    
    
    input_dim = 784
    nb_epoch = 20
    batch_size=128
    
    datagen = DataGenerator(batch_size)
    
    # network definition
    base_network = create_base_network(input_dim)
    
    input_a = Input(shape=(input_dim,))
    input_b = Input(shape=(input_dim,))
    
    # because we re-use the same instance `base_network`,
    # the weights of the network
    # will be shared across the two branches
    processed_a = base_network(input_a)
    processed_b = base_network(input_b)
    
    distance = Lambda(euclidean_distance, output_shape=eucl_dist_output_shape)([processed_a, processed_b])
    
    model = Model(input=[input_a, input_b], output=distance)
    
    # train
    rms = RMSprop()
    model.compile(loss=contrastive_loss, optimizer=rms)
    model.fit_generator(generator=datagen.next_train(), samples_per_epoch=datagen.samples_per_train, nb_epoch=nb_epoch, validation_data=datagen.next_val(), nb_val_samples=datagen.samples_per_val)
    

    【讨论】:

    • 谢谢!我还有几个问题: 1. 生成器应该做一些随机化吗?还是fit_generator 函数可以做到这一点? 1. 如果我正确理解generator 决定每时每刻何时加载哪些图像?
    • fit_generator 仅将generator= 函数和validation_data= 函数作为基于pickle_safe 参数的线程/进程运行,并提供fit 函数的回调设施。任何数据混洗都需要在生成器本身中完成。
    • 这真的回答了这个问题吗?我的意思是,只要您致电mnist.load_data,您就正在加载整个数据集,对吗?这可能是大型数据集的问题。我们可以使用flow_from_directory或其他方法来加载数据集吗?
    猜你喜欢
    • 2022-06-18
    • 1970-01-01
    • 2019-05-27
    • 2018-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-03
    • 1970-01-01
    相关资源
    最近更新 更多