我目前正在实现连续深度匹配模型(https://arxiv.org/abs/1909.00385)使用tensorflow 2.3。我通过对keras.layers.Layer
进行子类化,将预处理层作为模型的一部分。
下面列出了代码的预处理部分
class Preprocessing(keras.layers.Layer):
def __init__(self, str_columns, hash_bins, float_columns, float_buckets, embedding_dim, user_columns, short_seq_columns, prefer_seq_columns, item_key_feats,
item_key_hash_bucket_size, series_feats, series_feats_hash_bucket_size, deviceid_num, device_list, **kwargs):
super(Preprocessing, self).__init__(**kwargs)
self.str_columns = str_columns
self.hash_bins = hash_bins
self.float_columns = float_columns
self.float_buckets = float_buckets
self.embedding_dim = embedding_dim
self.user_columns = user_columns
self.short_seq_columns = short_seq_columns
self.prefer_seq_columns = prefer_seq_columns
self.item_key_feats = item_key_feats
self.item_key_hash_bucket_size = item_key_hash_bucket_size
self.series_feats = series_feats
self.series_feats_hash_bucket_size = series_feats_hash_bucket_size
self.deviceid_num = deviceid_num
self.device_list = device_list
self.user_outputs = {}
self.short_outputs = {}
self.prefer_outputs = {}
deviceid_lookup = keras.layers.experimental.preprocessing.StringLookup(vocabulary=device_list, mask_token=None, oov_token="-1")
deviceid_embedding = keras.layers.Embedding(input_dim=deviceid_num, output_dim=embedding_dim)
item_key_hashing = keras.layers.experimental.preprocessing.Hashing(num_bins=item_key_hash_bucket_size)
item_key_embedding = keras.layers.Embedding(input_dim=item_key_hash_bucket_size, output_dim=embedding_dim)
series_hashing = keras.layers.experimental.preprocessing.Hashing(num_bins=series_feats_hash_bucket_size)
series_embedding = keras.layers.Embedding(input_dim=series_feats_hash_bucket_size, output_dim=embedding_dim)
for i in str_columns:
if i == "device_id":
process = [deviceid_lookup, deviceid_embedding]
elif i in item_key_feats:
process = [item_key_hashing, item_key_embedding]
elif i in series_feats:
process = [series_hashing, series_embedding]
else:
hashing = keras.layers.experimental.preprocessing.Hashing(num_bins=hash_bins[i])
embedding = keras.layers.Embedding(input_dim=hash_bins[i], output_dim=embedding_dim)
process = [hashing, embedding]
if i in user_columns:
self.user_outputs[i] = process
if i in short_seq_columns:
self.short_outputs[i] = process
if i in prefer_seq_columns:
self.prefer_outputs[i] = process
for l in float_columns:
discrete = keras.layers.experimental.preprocessing.Discretization(bins=float_buckets[l])
embedding = keras.layers.Embedding(input_dim=len(float_buckets[l]) + 1, output_dim=embedding_dim)
if l in user_columns:
self.user_outputs[l] = [discrete, embedding]
if l in short_seq_columns:
self.short_outputs[l] = [discrete, embedding]
if l in prefer_seq_columns:
self.prefer_outputs[l] = [discrete, embedding]
@staticmethod
def get_embedding(input_tmp, name, embed_dict):
func = embed_dict[name]
if len(func) < 2:
print(func)
raise Exception('Not enough function to retrieve embedding')
output = func[0](input_tmp)
output = func[1](output)
return output
def call(self, inputs):
user_embedding = tf.concat([tf.reduce_mean(self.get_embedding(inputs[i], i, self.user_outputs), axis=[1, 2]) for i in self.user_columns], axis=-1)
short_embedding = tf.concat([tf.squeeze(self.get_embedding(inputs[l], l, self.short_outputs), axis=1).to_tensor() for l in self.short_seq_columns], axis=-1)
prefer_embedding = {k: tf.squeeze(self.get_embedding(inputs[k], k, self.prefer_outputs).to_tensor(), axis=1) for k in self.prefer_seq_columns}
return user_embedding, short_embedding, prefer_embedding
还有我的输入代码:
def read_row(csv_row):
record_defaults = [[0.]] * numeric_feature_size + [['']] * category_feature_size + [['0-0']] + [['0']]
row = tf.io.decode_csv(csv_row, record_defaults=record_defaults, field_delim='', use_quote_delim=False)
features = []
for i, feature in enumerate(row):
if i < numeric_feature_size:
features.append(feature)
elif i < numeric_feature_size + category_feature_size:
tmp_tf = tf.strings.split([feature], ";")
features.append(tmp_tf)
res = OrderedDict(zip(numeric_columns + category_columns, features))
res['target'] = [tf.cast(row[-2], tf.string)]
return res
代码的另一部分没有在这里给出,因为我相信这是正确的,可能太多了,无法在这里列出。在使用model.compile
和model.fit
进行训练的过程中,模型正常工作,但是,在我使用model.save(path)
保存它之后,生成的Graph得到了许多未知输入,并且没有保存任何输入名称。
saved_model_cli show --dir ./ --tag_set serve --signature_def serving_default
The given SavedModel SignatureDef contains the following input(s):
inputs['args_0'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: serving_default_args_0:0
inputs['args_0_1'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_1:0
inputs['args_0_10'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_10:0
inputs['args_0_11'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_11:0
inputs['args_0_12'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: serving_default_args_0_12:0
inputs['args_0_13'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_13:0
inputs['args_0_14'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_14:0
inputs['args_0_15'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: serving_default_args_0_15:0
inputs['args_0_16'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_16:0
inputs['args_0_17'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_17:0
inputs['args_0_18'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: serving_default_args_0_18:0
inputs['args_0_19'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_19:0
inputs['args_0_2'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_2:0
inputs['args_0_20'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_20:0
inputs['args_0_21'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: serving_default_args_0_21:0
inputs['args_0_22'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_22:0
inputs['args_0_23'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_23:0
inputs['args_0_24'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: serving_default_args_0_24:0
inputs['args_0_25'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_25:0
inputs['args_0_26'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_26:0
inputs['args_0_27'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: serving_default_args_0_27:0
inputs['args_0_28'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_28:0
inputs['args_0_29'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_29:0
inputs['args_0_3'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: serving_default_args_0_3:0
inputs['args_0_30'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: serving_default_args_0_30:0
inputs['args_0_31'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_31:0
inputs['args_0_32'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_32:0
inputs['args_0_33'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: serving_default_args_0_33:0
inputs['args_0_34'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_34:0
inputs['args_0_35'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_35:0
inputs['args_0_36'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: serving_default_args_0_36:0
inputs['args_0_37'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_37:0
inputs['args_0_38'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_38:0
inputs['args_0_39'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: serving_default_args_0_39:0
inputs['args_0_4'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_4:0
inputs['args_0_40'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_40:0
inputs['args_0_41'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_41:0
inputs['args_0_42'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: serving_default_args_0_42:0
inputs['args_0_43'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_43:0
inputs['args_0_44'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_44:0
inputs['args_0_45'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: serving_default_args_0_45:0
inputs['args_0_46'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_46:0
inputs['args_0_47'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_47:0
inputs['args_0_48'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: serving_default_args_0_48:0
inputs['args_0_49'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_49:0
inputs['args_0_5'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_5:0
inputs['args_0_50'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_50:0
inputs['args_0_6'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: serving_default_args_0_6:0
inputs['args_0_7'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_7:0
inputs['args_0_8'] tensor_info:
dtype: DT_INT64
shape: (-1)
name: serving_default_args_0_8:0
inputs['args_0_9'] tensor_info:
dtype: DT_STRING
shape: (-1)
name: serving_default_args_0_9:0
The given SavedModel SignatureDef contains the following output(s):
outputs['output_1'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 64)
name: StatefulPartitionedCall:0
在这个模型中,我只使用了dtype为tf.string
的分类特征,所以所有dtype为DT_INT64
的输入都不是我的模型输入的一部分。
有人能帮我吗?
我终于完成了这项工作。
错误来自我的tf.io.decode_csv
方法。默认情况下,tf.strings.split()
将返回一个RaggedTensor
,其中还将包含两个int变量。这就是为什么输入签名包含17字符串类型和34 int类型。RaggedTensor
还会损害keras序列化,这就是为什么所有输入名称都丢失的原因。我将所有的RaggedTensor
转换为EagerTensor
,所有的事情都成功了。
然而,这并不是我在尝试加载模型时遇到的唯一错误。
我还遇到The same saveable will be restored with two names
错误,我花了很多时间来解决它。事实证明,这是keras.layers.experimental.preprocessing
模块的错误,其中的相同函数不能使用两次,因为变量将被记录为相同的名称,并导致不可加载的savedmodel
。
这很容易,我用多种方法进行了测试,发现名称是发生的最重要的事情。
带有lstm单元的自定义类和RNN不起作用,我也花时间测试,因为跟踪和RNN需要很多时间。
[样本]:
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""
: Model Initialize
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""
model = tf.keras.models.Sequential([
tf.keras.layers.InputLayer(input_shape=( 32, 32, 3 ), batch_size=1, name='layer_input'),
tf.keras.layers.Normalization(mean=3., variance=2., name='layer_normalize_1'),
tf.keras.layers.Normalization(mean=4., variance=6., name='layer_normalize_2'),
tf.keras.layers.Conv2DTranspose(2, 3, activation='relu', padding="same", name='layer_conv2dT_1'),
tf.keras.layers.MaxPooling2D(pool_size=(2, 2), strides=(1, 1), padding='valid', name='layer_maxpool_1'),
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(4 * 256, name='layer_dense_1'),
tf.keras.layers.Reshape((4 * 256, 1)),
tf.keras.layers.LSTM(128, name='layer_4', return_sequences=True, return_state=False),
tf.keras.layers.LSTM(128, name='layer_5'),
tf.keras.layers.Dropout(0.2),
])
model.add(tf.keras.layers.Flatten())
model.add(tf.keras.layers.Dense(64, activation='relu', name='layer_dense_2'))
model.add(tf.keras.layers.Dense(7, name='layer_dense_3'))
model.summary()
# Loads the weights
if exists(target_saved_model) :
model = load_model(target_saved_model)
print("model load: " + target_saved_model)
input("Press Any Key!")
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""
: Training
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""
history = model.fit(x_train, y_train, epochs=10, batch_size=1 ,validation_data=(x_test, y_test))
model.save(target_saved_model)
print('.................')
[输出]:1。来自层lstm的错误消息
Epoch 10/10
20/20 [==============================] - 1s 71ms/step - loss: 0.7332 - val_loss: 0.8078
2022-04-06 23:23:26.017439: W tensorflow/python/util/util.cc:368] Sets are not currently considered sequences, but this may change in the future, so consider avoiding using them.
WARNING:absl:Found untraced functions such as lstm_cell_layer_call_fn, lstm_cell_layer_call_and_return_conditional_losses, lstm_cell_1_layer_call_fn, lstm_cell_1_layer_call_and_return_conditional_losses while saving (showing 4 of 4). These functions will not be directly callable after loading.
WARNING:absl:<keras.layers.recurrent.LSTMCell object at 0x00000145F1C83130> has the same name 'LSTMCell' as a built-in Keras object. Consider renaming <class 'keras.layers.recurrent.LSTMCell'> to avoid naming conflicts when loading with `tf.keras.models.load_model`. If renaming is not possible, pass the object in the `custom_objects` parameter of the load function.
WARNING:absl:<keras.layers.recurrent.LSTMCell object at 0x00000145F1C83C10> has the same name 'LSTMCell' as a built-in Keras object. Consider renaming <class 'keras.layers.recurrent.LSTMCell'> to avoid naming conflicts when loading with `tf.keras.models.load_model`. If renaming is not possible, pass the object in the `custom_objects` parameter of the load function.
...
[输出2]:2。错误消息已删除
Epoch 10/10
100/100 [==============================] - 6s 63ms/step - loss: 0.3954 - val_loss: 0.5108
.................
...