Хобрук: Ваш путь к мастерству в программировании

ValueError при загрузке ранее сохраненной переобученной модели VGG16 с использованием Keras

Я использую архитектуру VGG16 в Keras, которую я переобучил, чтобы она соответствовала моим потребностям, следующим образом:

vgg16_model = keras.applications.vgg16.VGG16()
model = Sequential()
for layer in vgg16_model.layers:
    model.add(layer)


model.layers.pop()
for layer in model.layers:
    layer.trainable = False

model.add(Dense(3, activation='softmax'))

model.compile(Adam(lr=.0001), loss='categorical_crossentropy', metrics=['accuracy'])

Затем я обучаю модель, а затем сохраняю всю модель так, как это предлагается в документации keras:

from keras.models import load_model

model.save('my_model_vgg16.h5')  # creates a HDF5 file

при загрузке модели так:

model = load_model('my_model_vgg16.h5')

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

ValueError: Dimension 0 in both shapes must be equal, but are 4096 and 1000 for 'Assign_30' (op: 'Assign') with input shapes: [4096,3], [1000,3].

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

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

Для справки, весь журнал ошибок консоли выглядит так:

---------------------------------------------------------------------------
InvalidArgumentError                      Traceback (most recent call last)
~\Anaconda2\envs\tensorflow-gpu\lib\site-packages\tensorflow\python\framework\common_shapes.py in _call_cpp_shape_fn_impl(op, input_tensors_needed, input_tensors_as_shapes_needed, require_shape_fn)
    685           graph_def_version, node_def_str, input_shapes, input_tensors,
--> 686           input_tensors_as_shapes, status)
    687   except errors.InvalidArgumentError as err:

~\Anaconda2\envs\tensorflow-gpu\lib\site-packages\tensorflow\python\framework\errors_impl.py in __exit__(self, type_arg, value_arg, traceback_arg)
    472             compat.as_text(c_api.TF_Message(self.status.status)),
--> 473             c_api.TF_GetCode(self.status.status))
    474     # Delete the underlying status object from memory otherwise it stays alive

InvalidArgumentError: Dimension 0 in both shapes must be equal, but are 4096 and 1000 for 'Assign_30' (op: 'Assign') with input shapes: [4096,3], [1000,3].

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
<ipython-input-5-a2d2e98db4b6> in <module>()
      1 from keras.models import load_model
----> 2 loaded_model = load_model('my_model_vgg16.h5')
      3 print("Loaded Model from disk")
      4 
      5 #compile and evaluate loaded model

~\Anaconda2\envs\tensorflow-gpu\lib\site-packages\keras\models.py in load_model(filepath, custom_objects, compile)
    244 
    245         # set weights
--> 246         topology.load_weights_from_hdf5_group(f['model_weights'], model.layers)
    247 
    248         # Early return if compilation is not required.

~\Anaconda2\envs\tensorflow-gpu\lib\site-packages\keras\engine\topology.py in load_weights_from_hdf5_group(f, layers)
   3164                              ' elements.')
   3165         weight_value_tuples += zip(symbolic_weights, weight_values)
-> 3166     K.batch_set_value(weight_value_tuples)
   3167 
   3168 

~\Anaconda2\envs\tensorflow-gpu\lib\site-packages\keras\backend\tensorflow_backend.py in batch_set_value(tuples)
   2363                 assign_placeholder = tf.placeholder(tf_dtype,
   2364                                                     shape=value.shape)
-> 2365                 assign_op = x.assign(assign_placeholder)
   2366                 x._assign_placeholder = assign_placeholder
   2367                 x._assign_op = assign_op

~\Anaconda2\envs\tensorflow-gpu\lib\site-packages\tensorflow\python\ops\variables.py in assign(self, value, use_locking)
    571       the assignment has completed.
    572     """
--> 573     return state_ops.assign(self._variable, value, use_locking=use_locking)
    574 
    575   def assign_add(self, delta, use_locking=False):

~\Anaconda2\envs\tensorflow-gpu\lib\site-packages\tensorflow\python\ops\state_ops.py in assign(ref, value, validate_shape, use_locking, name)
    274     return gen_state_ops.assign(
    275         ref, value, use_locking=use_locking, name=name,
--> 276         validate_shape=validate_shape)
    277   return ref.assign(value)

~\Anaconda2\envs\tensorflow-gpu\lib\site-packages\tensorflow\python\ops\gen_state_ops.py in assign(ref, value, validate_shape, use_locking, name)
     54     _, _, _op = _op_def_lib._apply_op_helper(
     55         "Assign", ref=ref, value=value, validate_shape=validate_shape,
---> 56         use_locking=use_locking, name=name)
     57     _result = _op.outputs[:]
     58     _inputs_flat = _op.inputs

~\Anaconda2\envs\tensorflow-gpu\lib\site-packages\tensorflow\python\framework\op_def_library.py in _apply_op_helper(self, op_type_name, name, **keywords)
    785         op = g.create_op(op_type_name, inputs, output_types, name=scope,
    786                          input_types=input_types, attrs=attr_protos,
--> 787                          op_def=op_def)
    788       return output_structure, op_def.is_stateful, op
    789 

~\Anaconda2\envs\tensorflow-gpu\lib\site-packages\tensorflow\python\framework\ops.py in create_op(self, op_type, inputs, dtypes, input_types, name, attrs, op_def, compute_shapes, compute_device)
   2956         op_def=op_def)
   2957     if compute_shapes:
-> 2958       set_shapes_for_outputs(ret)
   2959     self._add_op(ret)
   2960     self._record_op_seen_by_control_dependencies(ret)

~\Anaconda2\envs\tensorflow-gpu\lib\site-packages\tensorflow\python\framework\ops.py in set_shapes_for_outputs(op)
   2207       shape_func = _call_cpp_shape_fn_and_require_op
   2208 
-> 2209   shapes = shape_func(op)
   2210   if shapes is None:
   2211     raise RuntimeError(

~\Anaconda2\envs\tensorflow-gpu\lib\site-packages\tensorflow\python\framework\ops.py in call_with_requiring(op)
   2157 
   2158   def call_with_requiring(op):
-> 2159     return call_cpp_shape_fn(op, require_shape_fn=True)
   2160 
   2161   _call_cpp_shape_fn_and_require_op = call_with_requiring

~\Anaconda2\envs\tensorflow-gpu\lib\site-packages\tensorflow\python\framework\common_shapes.py in call_cpp_shape_fn(op, require_shape_fn)
    625     res = _call_cpp_shape_fn_impl(op, input_tensors_needed,
    626                                   input_tensors_as_shapes_needed,
--> 627                                   require_shape_fn)
    628     if not isinstance(res, dict):
    629       # Handles the case where _call_cpp_shape_fn_impl calls unknown_shape(op).

~\Anaconda2\envs\tensorflow-gpu\lib\site-packages\tensorflow\python\framework\common_shapes.py in _call_cpp_shape_fn_impl(op, input_tensors_needed, input_tensors_as_shapes_needed, require_shape_fn)
    689       missing_shape_fn = True
    690     else:
--> 691       raise ValueError(err.message)
    692 
    693   if missing_shape_fn:

ValueError: Dimension 0 in both shapes must be equal, but are 4096 and 1000 for 'Assign_30' (op: 'Assign') with input shapes: [4096,3], [1000,3].

Ответы:


1

Проблема со строкой model.layers.pop(). При извлечении слоя непосредственно из списка model.layers топология этой модели не обновляется соответствующим образом. Таким образом, все последующие операции будут неправильными при неправильном определении модели.

В частности, когда вы добавляете слой с model.add(layer), список model.outputs обновляется и становится выходным тензором этого слоя. Вы можете найти следующие строки в исходном коде Sequential.add():

        output_tensor = layer(self.outputs[0])
        # ... skipping irrelevant lines
        self.outputs = [output_tensor]

Однако при вызове model.layers.pop() model.outputs не обновляется соответствующим образом. В результате следующий добавленный слой будет вызываться с неправильным входным тензором (поскольку self.outputs[0] по-прежнему является выходным тензором удаленного слоя).

Это можно продемонстрировать следующими строками:

model = Sequential()
for layer in vgg16_model.layers:
    model.add(layer)

model.layers.pop()
model.add(Dense(3, activation='softmax'))

print(model.layers[-1].input)
# => Tensor("predictions_1/Softmax:0", shape=(?, 1000), dtype=float32)
# the new layer is called on a wrong input tensor

print(model.layers[-1].kernel)
# => <tf.Variable 'dense_1/kernel:0' shape=(1000, 3) dtype=float32_ref>
# the kernel shape is also wrong

Неправильная форма ядра является причиной того, что вы видите ошибку о несовместимости форм [4096,3] и [1000,3].

Чтобы решить эту проблему, просто не добавляйте последний слой в модель Sequential.

model = Sequential()
for layer in vgg16_model.layers[:-1]:
    model.add(layer)
28.01.2018
  • круто, теперь работает! большое спасибо за этот отличный ответ! 28.01.2018
  • Новые материалы

    Не зря же это называют интеллектом
    Стек — C#, Oracle Опыт — 4 года Работа — Разведывательный корпус Мне пора служить Может быть, я немного приукрашиваю себя, но там, где я живу, есть обязательная военная служба на 3..

    LeetCode Проблема 41. Первый пропущенный положительный результат
    LeetCode Проблема 41. Первый пропущенный положительный результат Учитывая несортированный массив целых чисел, найдите наименьшее пропущенное положительное целое число. Пример 1: Input:..

    Расистский и сексистский робот, обученный в Интернете
    Его ИИ основан на предвзятых данных, которые создают предрассудки. Он словно переходит из одного эпизода в другой из серии Черное зеркало , а вместо этого представляет собой хронику..

    Управление состоянием в микрофронтендах
    Стратегии бесперебойного сотрудничества Микро-фронтенды — это быстро растущая тенденция в сфере фронтенда, гарантирующая, что удовольствие не ограничивается исключительно бэкэнд-системами..

    Декларативное и функциональное программирование в стиле LINQ с использованием JavaScript с использованием каррирования и генератора ...
    LINQ - одна из лучших функций C #, которая обеспечивает элегантный способ написания кода декларативного и функционального стиля, который легко читать и понимать. Благодаря таким функциям ES6,..

    Структуры данных в C ++ - Часть 1
    Реализация общих структур данных в C ++ C ++ - это расширение языка программирования C, которое поддерживает создание классов, поэтому оно известно как C с классами . Он используется для..

    Как я опубликовал свое первое приложение в App Store в 13 лет
    Как все началось Все началось три года назад летом после моего четвертого класса в начальной школе. Для меня, четвертого класса, лето кажется бесконечным, пока оно не закончится, и мой отец..