【问题标题】:Keras layer for slicing image data into sliding windows用于将图像数据切片到滑动窗口中的 Keras 层
【发布时间】:2019-09-18 07:37:55
【问题描述】:

我有一组图像,所有的宽度都不同,但固定高度设置为 100 像素和 3 个深度通道。我的任务是分类图像中的每条垂直线是否有趣。为此,我在其 10 条前身和后继产品线的背景下查看了该产品线。想象一下算法从左到右扫描图像,检测包含兴趣点的垂直线。

我第一次尝试这样做是在将数据输入 Keras 模型之前使用 numpy 手动剪切这些滑动窗口。像这样:

# Pad left and right
s = np.repeat(D[:1], 10, axis = 0)
e = np.repeat(D[-1:], 10, axis = 0)
# D now has shape (w + 20, 100, 3)
D = np.concatenate((s, D, e))
# Sliding windows creation trick from SO question
idx = np.arange(21)[None,:] + np.arange(len(D) - 20)[:,None]
windows = D[indexer]

然后,所有图像中所有垂直线的所有窗口和所有 ground truth 0/1 值将连接成两个非常长的数组。

我已经验证了这在原则上是可行的。我将每个窗口提供给一个 Keras 层,如下所示:

Conv2D(20, (5, 5), input_shape = (21, 100, 3), padding = 'valid', ...)

但是窗口化会导致内存使用量增加 21 倍,因此这样做变得不切实际。但我认为我的场景在机器学习中很常见,所以 Keras 中必须有一些标准方法才能有效地做到这一点?例如,我想向 Keras 提供我的原始图像数据(w、100、80)并告诉它滑动窗口的大小是多少,然后让它找出其余的。我查看了一些示例代码,但我是菜鸟,所以我不明白。

【问题讨论】:

    标签: keras deep-learning conv-neural-network sliding-window


    【解决方案1】:

    不幸的是,这不是一个简单的问题,因为它可能涉及为您的 Keras 模型使用可变大小的输入。虽然我认为可以通过正确使用占位符来做到这一点,但这对于初学者来说肯定不是一个起点。您的另一个选择是数据生成器。与许多计算密集型任务一样,通常需要在计算速度和内存需求之间进行权衡,使用生成器的计算量更大,它将完全在您的 cpu 上完成(没有 gpu 加速),但不会增加内存.

    数据生成器的意义在于,它会一次将操作应用于图像以生成批次,然后在该批次上进行训练,然后释放内存 - 因此您最终只会保留一批数据随时在记忆中。不幸的是,如果你有一个耗时的生成,那么这会严重影响性能。

    生成器将是一个python生成器(使用'yield'关键字)并且预计会生成单批次数据,keras非常擅长使用任意批次大小,因此您始终可以使一个图像产生一批,尤其是开始。

    这是 fit_generator 上的 keras 页面 - 我警告你,这很快就会开始变得很多工作,考虑购买更多内存: https://keras.io/models/model/#fit_generator

    好吧,我会为你做的:P

        import numpy as np
        import pandas as pd
        import keras
        from keras.models import Model, model_from_json
        from keras.layers import Dense, Concatenate, Multiply,Add, Subtract, Input, Dropout, Lambda, Conv1D, Flatten
        from tensorflow.python.client import device_lib
        # check for my gpu 
        print(device_lib.list_local_devices())
    
    
        # make some fake image data
    
        # 1000 random widths
        data_widths = np.floor(np.random.random(1000)*100)
    
        # producing 1000 random images with dimensions w x 100 x 3
        # and a vector of which vertical lines are interesting
        # I assume your data looks like this
        images = []
        interesting = []
        for w in data_widths:
            images.append(np.random.random([int(w),100,3]))
            interesting.append(np.random.random(int(w))>0.5)
    
        # this is a generator
        def image_generator(images, interesting):
            num = 0
            while num < len(images):
                windows = None
                truth = None
    
                D = images[num]
                # this should look familiar
    
                # Pad left and right
                s = np.repeat(D[:1], 10, axis = 0)
                e = np.repeat(D[-1:], 10, axis = 0)
                # D now has shape (w + 20, 100, 3)
                D = np.concatenate((s, D, e))
                # Sliding windows creation trick from SO question
                idx = np.arange(21)[None,:] + np.arange(len(D) - 20)[:,None]
                windows = D[idx]
                truth = np.expand_dims(1*interesting[num],axis=1)
                yield (windows, truth)
                num+=1
                # the generator MUST loop
                if num == len(images):
                    num = 0
    
        # basic model - replace with your own
        input_layer = Input(shape = (21,100,3), name = "input_node")
        fc = Flatten()(input_layer)
        fc = Dense(100, activation='relu',name = "fc1")(fc)
        fc = Dense(50, activation='relu',name = "fc2")(fc)
        fc = Dense(10, activation='relu',name = "fc3")(fc)
        output_layer = Dense(1, activation='sigmoid',name = "output")(fc)
    
        model = Model(input_layer,output_layer)
        model.compile(optimizer="adam", loss='binary_crossentropy')
        model.summary()
    
        #and training
        training_history = model.fit_generator(image_generator(images, interesting),
                            epochs =5,
                            initial_epoch = 0,
                            steps_per_epoch=len(images),
                            verbose=1
                           )
    

    【讨论】:

    • 你能告诉我怎么做吗?我仍然认为我的用例一定很常见。 :)
    • 如果您愿意,您甚至可以将图像加载到生成器中,以释放更多内存。但我会把那个留给你
    • 当您尝试使用线程或多处理来释放更多内存并从您的机器中挤出更多性能时,我所说的“大量工作”就出现了——这个例子实际上是很容易
    猜你喜欢
    • 2018-12-01
    • 1970-01-01
    • 2014-06-02
    • 1970-01-01
    • 2019-11-16
    • 2017-08-19
    • 2013-02-25
    • 2015-05-30
    • 1970-01-01
    相关资源
    最近更新 更多