Добавление слоя предварительной обработки в модель keras и установка значений тензора

как лучше всего добавить слой предварительной обработки (например, вычесть среднее и разделить на std) в keras (v2.0.5) модель такая, что модель становится полностью автономной для развертывания (возможно, в среде C++). Я попробовал:

    def getmodel():
       model = Sequential()
       mean_tensor = K.placeholder(shape=(1,1,3), name="mean_tensor")
       std_tensor = K.placeholder(shape=(1,1,3), name="std_tensor")

       preproc_layer = Lambda(lambda x: (x - mean_tensor) / (std_tensor + K.epsilon()),
                              input_shape=im_shape)

       model.add(preproc_layer)

       # Build the remaining model, perhaps set weights,
       ...

       return model

затем где-то еще установите среднее значение/std на модели. Я нашел значение функция так пробовала следующее:

m = getmodel()
mean, std = get_mean_std(..)

graph = K.get_session().graph
mean_tensor = graph.get_tensor_by_name("mean_tensor:0")
std_tensor = graph.get_tensor_by_name("std_tensor:0")

K.set_value(mean_tensor, mean)
K.set_value(std_tensor, std)
на set_value выдает
AttributeError: 'Tensor' object has no attribute 'assign'

так set_value не работайте как (ограниченные) документы. Как правильно это сделать? Получите сеанс TF, оберните весь код обучения в with (session) и использовать feed_dict? Я бы подумал, что будет собственный способ keras установить значения тензора.

вместо использования заполнителя я попытался установить среднее/std при построении модели, используя либо K.variable или K.constant:

mean_tensor = K.variable(mean, name="mean_tensor")
std_tensor = K.variable(std, name="std_tensor")

это позволяет избежать любых set_value проблемы. Хотя я замечаю, что если я попытаюсь обучить эту модель (которая Я знаю, что это не особенно эффективно, поскольку вы повторно делаете нормализацию для каждого изображения), он работает, но в конце первой эпохи ModelCheckpoint обработчик терпит неудачу с очень глубокой трассировкой стека:

...
File "/Users/dgorissen/Library/Python/2.7/lib/python/site-packages/keras/models.py", line 102, in save_model
  'config': model.get_config()
File "/Users/dgorissen/Library/Python/2.7/lib/python/site-packages/keras/models.py", line 1193, in get_config
  return copy.deepcopy(config)
File "/usr/local/Cellar/python/2.7.12_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy.py", line 163, in deepcopy
  y = copier(x, memo)
...
File "/usr/local/Cellar/python/2.7.12_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy.py", line 190, in deepcopy
  y = _reconstruct(x, rv, 1, memo)
File "/usr/local/Cellar/python/2.7.12_2/Frameworks/Python.framework/Versions/2.7/lib/python2.7/copy.py", line 343, in _reconstruct
  y.__dict__.update(state)
AttributeError: 'NoneType' object has no attribute 'update'

обновление 1:

я также попробовал другой подход. Обучите модель как обычно, а затем просто добавьте вторую модель, которая выполняет предварительную обработку:

# Regular model, trained as usual
model = ...

# Preprocessing model
preproc_model = Sequential()
mean_tensor = K.constant(mean, name="mean_tensor")
std_tensor = K.constant(std, name="std_tensor")
preproc_layer = Lambda(lambda x: (x - mean_tensor) / (std_tensor + K.epsilon()),
                       input_shape=im_shape, name="normalisation")
preproc_model.add(preproc_layer)

# Prepend the preprocessing model to the regular model    
full_model = Model(inputs=[preproc_model.input],
              outputs=[model(preproc_model.output)])

# Save the complete model to disk
full_model.save('full_model.hdf5')

это, кажется, работает до save() вызова, который сбой с той же глубокой трассировкой стека, что и выше. Возможно,Lambda слой является проблемой, но juding от этот вопрос кажется, она должна хотя правильно serialise.

Итак, в целом, как добавить слой нормализации к модели keras без ущерба для возможности сериализации (и экспорта в pb)?

я уверен, что вы можете заставить его работать, опустившись до TF напрямую (например,этой теме, или с помощью tf.Transform) но я думал, что в керасе это возможно.

обновление 2:

поэтому я обнаружил, что глубокой трассировки стека можно избежать, выполнив

def foo(x):
    bar = K.variable(baz, name="baz")
    return x - bar

так что определение bar внутри функции вместо захвата из внешней области.

затем я обнаружил, что могу сохранить на диск, но не могу загрузить с диска. Вокруг этого есть набор проблем github. Я использовал обходной путь, указанный в #5396 чтобы передать все переменные в качестве аргументов, это позволило мне сохранить и загрузить.

думая, что я почти там, я продолжил свой подход от обновление 1 выше штабелировать pre-processing модель перед натренированной моделью. Это привело к Model is not compiled ошибки. Работал вокруг них, но в конце концов мне никогда не удавалось заставить работать следующее:

  • построить и обучить модель
  • сохранить его на диск
  • загрузите его, добавьте a предобработка модели
  • экспорт сложенной модели на диск в виде замороженного файла pb
  • загрузите замороженный pb с диска
  • применить его на некоторых невидимых данных

я получил его до точки, где не было ошибок, но не мог получить тензоры нормализации для распространения до замороженного pb. Потратив на это слишком много времени, я сдался и перешел к несколько менее элегантному подходу:

  • построить модель с операции предварительной обработки в модели с самого начала, но установлены в no-op (mean=0, std=1)
  • обучите модель, создайте идентичную модель, но на этот раз с правильными значениями для mean/std.
  • перенос веса
  • экспорт и замораживание модели в pb

все это теперь полностью работает, как ожидалось. Небольшие накладные расходы на обучение, но незначительные для меня.

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

примет ответ @ Daniel's, поскольку он заставил меня идти в правильном направлении.

вопрос:

1 ответов


при создании переменной вы должны дать ей "значение", а не форму:

mean_tensor = K.variable(mean, name="mean_tensor")
std_tensor = K.variable(std, name="std_tensor")

теперь, в Keras, вам не нужно иметь дело с сессией, графиком и тому подобными вещами. Вы работаете только со слоями, а внутри лямбда-слоев (или функций потерь) вы можете работать с тензорами.

для нашего лямбда-слоя нам нужна более сложная функция, потому что фигуры должны совпадать перед вычислением. Так как я не знаю im_shape, я предположил, что у него было 3 размеры:

def myFunc(x):

    #reshape x in a way it's compatible with the tensors mean and std:
    x = K.reshape(x,(-1,1,1,3)) 
        #-1 is like a wildcard, it will be the value that matches the rest of the given shape.     
        #I chose (1,1,3) because it's the same shape of mean_tensor and std_tensor

    result = (x - mean_tensor) / (std_tensor + K.epsilon())

    #now shape it back to the same shape it was before (which I don't know)    
    return K.reshape(result,(-1,im_shape[0], im_shape[1], im_shape[2]))
        #-1 is still necessary, it's the batch size

теперь мы создаем лямбда-слой, учитывая, что ему также нужна выходная форма (из-за вашей пользовательской операции система не обязательно знает выходную форму)

model.add(Lambda(myFunc,input_shape=im_shape, output_shape=im_shape))

после этого просто скомпилируйте модель и обучите ее. (Часто с model.compile(...) и model.fit(...))


если вы хотите включить все, включая предварительную обработку внутри функции, ok тоже:

def myFunc(x):

    mean_tensor = K.mean(x,axis=[0,1,2]) #considering shapes of (size,width, heigth,channels)    
    std_tensor = K.std(x,axis=[0,1,2])

    x = K.reshape(x, (-1,3)) #shapes of mean and std are (3,) here.    
    result = (x - mean_tensor) / (std_tensor + K.epsilon())

    return K.reshape(result,(-1,width,height,3))

теперь, все это дополнительный расчет в вашей модели и будет потреблять обработку. Лучше делать все, что выходит за рамки модели. Сначала создайте предварительно обработанные данные и сохраните их, а затем создайте модель без этого слоя предварительной обработки. Таким образом, вы получите более быструю модель. (Это может быть важно, если ваши данные или ваша модель слишком большой).