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