Слой Keras для разрезания данных изображения в скользящие окна - PullRequest
0 голосов
/ 29 апреля 2019

У меня есть набор изображений различной ширины, но с фиксированной высотой 100 пикселей и 3 каналами глубины. Моя задача - определить, интересна ли каждая вертикальная линия на изображении или нет. Чтобы сделать это, я смотрю на строку в контексте ее 10 предшествующих и последующих строк. Представьте себе алгоритм, проходящий слева направо по изображению, обнаруживающий вертикальные линии, содержащие точки интереса.

Моя первая попытка сделать это состояла в том, чтобы вручную вырезать эти скользящие окна, используя numpy, перед подачей данных в модель Keras. Как это:

# 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]

Тогда все окна и все значения истинной 0/1 для всех вертикальных линий на всех изображениях будут объединены в два очень длинных массива.

Я проверил, что это в принципе работает. Я вставил каждое окно в слой Keras, выглядящий так:

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

Но из-за оконного режима использование памяти увеличивается в 21 раз, поэтому делать это таким образом нецелесообразно. Но я думаю, что мой сценарий очень распространен в машинном обучении, поэтому в Керасе должен быть какой-то стандартный метод, чтобы сделать это эффективно? Например, я хотел бы передать Keras мои необработанные данные изображения (w, 100, 80) и сказать ему, каковы размеры скользящего окна, и позволить ему выяснить остальное. Я посмотрел пример кода, но я нуб мл, поэтому я не понимаю.

1 Ответ

1 голос
/ 29 апреля 2019

К сожалению, это не простая проблема, потому что она может включать использование переменных входных данных для вашей модели Keras. Хотя я думаю, что это можно сделать при правильном использовании заполнителей, для новичка определенно некуда начинать. Другой вариант - генератор данных. Как и во многих задачах, требующих большого объема вычислений, часто существует компромисс между скоростью вычислений и требованиями к памяти, использование генератора требует больше вычислительных ресурсов и будет полностью выполняться на вашем процессоре (без ускорения графического процессора), но это не приведет к увеличению объема памяти ,

Смысл генератора данных в том, что он будет применять операцию к изображениям по одному для создания пакета, затем обучаться этому пакету, а затем освобождать память - так что в итоге вы сохраняете только один пакет данных в памяти в любое время. К сожалению, если у вас много времени, то это может серьезно повлиять на производительность.

Генератор будет генератором Python (использующим ключевое слово yield) и, как ожидается, будет генерировать один пакет данных, keras очень хорошо использует произвольные размеры пакетов, поэтому вы всегда можете сделать одно изображение, чтобы получить один пакет, особенно для начала.

Вот страница keras на fit_generator - я предупреждаю вас, это очень быстро становится большой работой, подумайте о покупке большего количества памяти: 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
                       )
...