Я уже построил модель для своей рекомендательной системы, используя собственный Tensorflow 1.15, и теперь переносу весь исходный код в Tensorflow 2.1 с использованием Keras API, поскольку это рекомендуется в официальной документации Tensorflow и может облегчить мою жизнь для дальнейшего развития.
До сих пор я довольно много боролся с совместимостью между Keras и разреженной матрицей / тензором. Я должен использовать разреженный тензор, потому что мои данные слишком велики, чтобы их можно было обрабатывать с использованием нормальных плотных матриц.
Может кто-нибудь помочь мне объяснить, почему я получаю разное поведение между этими двумя кодами? Почему Tensorflow просит меня ввести значение в график, который я еще не запустил?
Вход 1:
user_inputs = tf.keras.layers.Input(
name="user_sparse_inputs",
shape=(10,),
dtype=tf.float32,
sparse=True,
batch_size=6,
)
print(user_inputs.shape)
user_repr = SparseEmbedding(n_components=5, name_suffix="emb")(user_inputs)
print(user_repr.shape)
model = tf.keras.models.Model(inputs=user_inputs, outputs=user_repr)
model.compile(optimizer='adam', loss='mse', metrics=['mse', 'mae'])
print(model.summary())
Выход 1:
(6, 10)
(6, 5)
Model: "model_1"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
user_sparse_inputs (InputLay [(6, 10)] 0
_________________________________________________________________
sparse_embedding_2 (SparseEm (6, 5) 50
=================================================================
Total params: 50
Trainable params: 50
Non-trainable params: 0
_________________________________________________________________
None
Вход 2:
user_inputs = tf.keras.layers.Input(
name="user_sparse_inputs",
shape=(6,10),
dtype=tf.float32,
sparse=True,
batch_size=1,
)
print(user_inputs.shape)
user_inputs_reshape = tf.sparse.reshape(user_inputs, [6, 10])
print(user_inputs_reshape.shape)
user_repr = SparseEmbedding(n_components=5, name_suffix="emb")
y = user_repr(user_inputs_reshape)
print(y.shape)
model = tf.keras.models.Model(inputs=user_inputs, outputs=y)
model.compile(optimizer='adam', loss='mse', metrics=['mse', 'mae'])
print(model.summary())
Выход 2:
(1, 6, 10)
(6, 10)
(6, 5)
---------------------------------------------------------------------------
InvalidArgumentError Traceback (most recent call last)
<ipython-input-6-81b2fd61efeb> in <module>
14 print(y.shape)
15
---> 16 model = tf.keras.models.Model(inputs=user_inputs, outputs=y)
17 model.compile(optimizer='adam', loss='mse', metrics=['mse', 'mae'])
18 print(model.summary())
~/.local/share/virtualenvs/perx-data-ml-wBZaki4L/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/training.py in __init__(self, *args, **kwargs)
144
145 def __init__(self, *args, **kwargs):
--> 146 super(Model, self).__init__(*args, **kwargs)
147 _keras_api_gauge.get_cell('model').set(True)
148 # initializing _distribution_strategy here since it is possible to call
~/.local/share/virtualenvs/perx-data-ml-wBZaki4L/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/network.py in __init__(self, *args, **kwargs)
167 'inputs' in kwargs and 'outputs' in kwargs):
168 # Graph network
--> 169 self._init_graph_network(*args, **kwargs)
170 else:
171 # Subclassed network
~/.local/share/virtualenvs/perx-data-ml-wBZaki4L/lib/python3.7/site-packages/tensorflow_core/python/training/tracking/base.py in _method_wrapper(self, *args, **kwargs)
455 self._self_setattr_tracking = False # pylint: disable=protected-access
456 try:
--> 457 result = method(self, *args, **kwargs)
458 finally:
459 self._self_setattr_tracking = previous_value # pylint: disable=protected-access
~/.local/share/virtualenvs/perx-data-ml-wBZaki4L/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/network.py in _init_graph_network(self, inputs, outputs, name, **kwargs)
270
271 if any(not hasattr(tensor, '_keras_history') for tensor in self.outputs):
--> 272 base_layer_utils.create_keras_history(self._nested_outputs)
273
274 self._base_init(name=name, **kwargs)
~/.local/share/virtualenvs/perx-data-ml-wBZaki4L/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/base_layer_utils.py in create_keras_history(tensors)
185 keras_tensors: The Tensors found that came from a Keras Layer.
186 """
--> 187 _, created_layers = _create_keras_history_helper(tensors, set(), [])
188 return created_layers
189
~/.local/share/virtualenvs/perx-data-ml-wBZaki4L/lib/python3.7/site-packages/tensorflow_core/python/keras/engine/base_layer_utils.py in _create_keras_history_helper(tensors, processed_ops, created_layers)
245 else:
246 with ops.init_scope():
--> 247 constants[i] = backend.function([], op_input)([])
248 processed_ops, created_layers = _create_keras_history_helper(
249 layer_inputs, processed_ops, created_layers)
~/.local/share/virtualenvs/perx-data-ml-wBZaki4L/lib/python3.7/site-packages/tensorflow_core/python/keras/backend.py in __call__(self, inputs)
3725 value = math_ops.cast(value, tensor.dtype)
3726 converted_inputs.append(value)
-> 3727 outputs = self._graph_fn(*converted_inputs)
3728
3729 # EagerTensor.numpy() will often make a copy to ensure memory safety.
~/.local/share/virtualenvs/perx-data-ml-wBZaki4L/lib/python3.7/site-packages/tensorflow_core/python/eager/function.py in __call__(self, *args, **kwargs)
1549 TypeError: For invalid positional/keyword argument combinations.
1550 """
-> 1551 return self._call_impl(args, kwargs)
1552
1553 def _call_impl(self, args, kwargs, cancellation_manager=None):
~/.local/share/virtualenvs/perx-data-ml-wBZaki4L/lib/python3.7/site-packages/tensorflow_core/python/eager/function.py in _call_impl(self, args, kwargs, cancellation_manager)
1589 raise TypeError("Keyword arguments {} unknown. Expected {}.".format(
1590 list(kwargs.keys()), list(self._arg_keywords)))
-> 1591 return self._call_flat(args, self.captured_inputs, cancellation_manager)
1592
1593 def _filtered_call(self, args, kwargs):
~/.local/share/virtualenvs/perx-data-ml-wBZaki4L/lib/python3.7/site-packages/tensorflow_core/python/eager/function.py in _call_flat(self, args, captured_inputs, cancellation_manager)
1690 # No tape is watching; skip to running the function.
1691 return self._build_call_outputs(self._inference_function.call(
-> 1692 ctx, args, cancellation_manager=cancellation_manager))
1693 forward_backward = self._select_forward_and_backward_functions(
1694 args,
~/.local/share/virtualenvs/perx-data-ml-wBZaki4L/lib/python3.7/site-packages/tensorflow_core/python/eager/function.py in call(self, ctx, args, cancellation_manager)
543 inputs=args,
544 attrs=("executor_type", executor_type, "config_proto", config),
--> 545 ctx=ctx)
546 else:
547 outputs = execute.execute_with_cancellation(
~/.local/share/virtualenvs/perx-data-ml-wBZaki4L/lib/python3.7/site-packages/tensorflow_core/python/eager/execute.py in quick_execute(op_name, num_outputs, inputs, attrs, ctx, name)
65 else:
66 message = e.message
---> 67 six.raise_from(core._status_to_exception(e.code, message), None)
68 except TypeError as e:
69 keras_symbolic_tensors = [
~/.local/share/virtualenvs/perx-data-ml-wBZaki4L/lib/python3.7/site-packages/six.py in raise_from(value, from_value)
InvalidArgumentError: You must feed a value for placeholder tensor 'user_sparse_inputs/values_3' with dtype float and shape [?]
[[node user_sparse_inputs/values_3 (defined at <ipython-input-6-81b2fd61efeb>:16) ]] [Op:__inference_keras_scratch_graph_263]
Function call stack:
keras_scratch_graph
Ниже пользовательский слой SparseEmbedding, который я написал сам, потому что Keras пока не поддерживает его с обычным слоем Embedded.
class SparseEmbedding(tf.keras.layers.Layer):
"""
Custom class for embedding sparse tensor Input.
The current version of TF does not support embedding for Sparse Tensor.
Ref: https://github.com/tensorflow/tensorflow/issues/33880
"""
def __init__(self, n_components, name_suffix):
super(SparseEmbedding, self).__init__()
self.n_components = n_components
self.name_suffix = name_suffix
def build(self, input_shape):
# Weights are normalized before building the variable
self.raw_weights = tf.random.normal(
[input_shape[-1], self.n_components], stddev=1.0, seed=SEED
)
self.normalized_weights = tf.nn.l2_normalize(self.raw_weights, 1)
# Create variable nodes
self.tf_linear_weights = tf.Variable(
self.normalized_weights,
name="embedding_weights_{}".format(self.name_suffix),
)
def call(self, inputs):
weights_mult = lambda x: tf.sparse.sparse_dense_matmul(
x, self.tf_linear_weights
)
outputs = tf.keras.layers.Lambda(weights_mult)(inputs)
return outputs
Большое спасибо.