tensorflow dataset构建输入时进行padding
2024-01-04 本文已影响0人
井底蛙蛙呱呱呱
序列特征作为一个常见的特征类型,在输入模型时常常需要进行padding,而基于模型的不同,padding方式也有差别。
1、在数据输入模型之前进行padding
当我们输入的序列特征是进行embedding pooling操作时,则需要提前进行补齐(padding)以保证所有样本特征维度相同。
此时可采用两种方式进行补齐:
- 1)对全部数据进行操作,短的补齐,长的裁剪;
- 2)在构建输入时进行batch padding。
方法1操作简单,但是由于需要提前处理全部数据,可能会占用较大的内存。
方法2在构建数据流输入时进行batch padding,每次仅需要padding到当前batch的最大长度即可,不仅占用内存更小,而且可保存更大的序列长度。
基于TensorFlow Dataset模块batch padding demo如下:
import numpy as np
import tensorflow as tf
def parse_record(serialized):
parse_features = tf.io.parse_single_example(serialized, features=Config.columns)
labels = parse_features["label"]
for col in Config.columns:
if isinstance(Config.columns[col], tf.VarLenFeature):
if Config.columns[col].dtype == tf.string:
parse_features[col] = tf.sparse_tensor_to_dense(parse_features[col], default_value="")
elif Config.columns[col].dtype==tf.float32:
parse_features[col] = tf.sparse_tensor_to_dense(parse_features[col], default_value=0.0)
else:
parse_features[col] = tf.sparse_tensor_to_dense(parse_features[col], default_value=-1)
return parse_features, labels
dataset = tf.data.Dataset.from_tensor_slices(["data/p.tfrecord.gz"])
dataset = dataset.apply(tf.contrib.data.parallel_interleave(
lambda filename: tf.data.TFRecordDataset(filename, compression_type="GZIP",
num_parallel_reads=2,
buffer_size=40960),
# 同时读取的文件数
cycle_length=1,
sloppy=True
))
# 定义各个字段padding shape
columns_padded_shapes = {
'income': [], # 单值类型,不需要进行padding
'sex': [],
'tvAge': [None], # 序列类型,None表示padding到batch最大长度,如需padding到指定长度,则需要预先处理全部数据并将所有数据最大长度裁剪到指定长度
'tvSex': [None],
'consumptionLevelOfBaidu': [None],
'interestPreferenceOfBaidu': [None]
...
}
# padding values可按需设置,默认字符类型补充空字符串,数字类型补0. 在配置的时候可与后面特征处理的时候缺失值的默认值保持一致。
# 此外,填充值可直接写数字如-1,也可写Tensor如 tf.constant(-1, dtype=tf.int64)
pad_vals = {
'income': tf.constant(-1, dtype=tf.int64),
'sex': tf.constant(-1, dtype=tf.int64),
"tvAge": tf.constant(-1, dtype=tf.int64),
"tvSex": tf.constant(-1, dtype=tf.int64),
'consumptionLevelOfBaidu': [None],
'interestPreferenceOfBaidu': [None],
"consumptionLevelOfBaidu": tf.constant(-1, dtype=tf.int64),
"interestPreferenceOfBaidu": tf.constant("UNK", dtype=tf.string)
...
}
dataset = dataset.repeat(4)
dataset = dataset.map(parse_record, num_parallel_calls=tf.data.experimental.AUTOTUNE)
# 注意这里padded_shapes和padding_values都是元组,这是因为parse_record函数的返回值有两个
dataset = dataset.padded_batch(32, padded_shapes=(columns_padded_shapes, []),
padding_values=(pad_vals, 0.0))
2、模型中进行padding
这种情况一般是序列作为RNN类网络的输入。
在特征编码后作为输入时:
sequence_feature_layer = tf.keras.experimental.SequenceFeatures(params['seq_feature_columns'])
sequence_input, sequence_length = sequence_feature_layer({key: features[key] for key in params['seq_features']})
sequence_mask = tf.sequence_mask(sequence_length)
lstm_layer = tf.keras.layers.LSTM(params['lstm_output_size'])
lstm_output = lstm_layer(sequence_input, mask=sequence_mask)