序列特征作為一個常見的特征類型,在輸入模型時常常需要進行padding,而基于模型的不同,padding方式也有差別。
1、在數(shù)據(jù)輸入模型之前進行padding
當我們輸入的序列特征是進行embedding pooling操作時,則需要提前進行補齊(padding)以保證所有樣本特征維度相同。
此時可采用兩種方式進行補齊:
- 1)對全部數(shù)據(jù)進行操作,短的補齊,長的裁剪;
- 2)在構建輸入時進行batch padding。
方法1操作簡單,但是由于需要提前處理全部數(shù)據(jù),可能會占用較大的內(nèi)存。
方法2在構建數(shù)據(jù)流輸入時進行batch padding,每次僅需要padding到當前batch的最大長度即可,不僅占用內(nèi)存更小,而且可保存更大的序列長度。
基于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),
# 同時讀取的文件數(shù)
cycle_length=1,
sloppy=True
))
# 定義各個字段padding shape
columns_padded_shapes = {
'income': [], # 單值類型,不需要進行padding
'sex': [],
'tvAge': [None], # 序列類型,None表示padding到batch最大長度,如需padding到指定長度,則需要預先處理全部數(shù)據(jù)并將所有數(shù)據(jù)最大長度裁剪到指定長度
'tvSex': [None],
'consumptionLevelOfBaidu': [None],
'interestPreferenceOfBaidu': [None]
...
}
# padding values可按需設置,默認字符類型補充空字符串,數(shù)字類型補0. 在配置的時候可與后面特征處理的時候缺失值的默認值保持一致。
# 此外,填充值可直接寫數(shù)字如-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函數(shù)的返回值有兩個
dataset = dataset.padded_batch(32, padded_shapes=(columns_padded_shapes, []),
padding_values=(pad_vals, 0.0))
2、模型中進行padding
這種情況一般是序列作為RNN類網(wǎng)絡的輸入。
在特征編碼后作為輸入時:
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)